Skip to contentSkip to conversation

This is a patch series of ethernet driver for Sunplus SP7021 SoC.

PATCH In review Wells Lu 10 participants 62 messages Nov 3, 2021 – Nov 29, 2021

Subsystems (4)
lore
Share & tools
lore

Wells Lu

author
#0 lore raw

cover This is a patch series of ethernet driver for Sunplus SP7021 SoC.

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-tibbo.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-l2sw.yaml          | 123 ++++
 MAINTAINERS                                        |   8 +
 drivers/net/ethernet/Kconfig                       |   1 +
 drivers/net/ethernet/Makefile                      |   1 +
 drivers/net/ethernet/sunplus/Kconfig               |  20 +
 drivers/net/ethernet/sunplus/Makefile              |   6 +
 drivers/net/ethernet/sunplus/l2sw_define.h         | 221 ++++++
 drivers/net/ethernet/sunplus/l2sw_desc.c           | 233 ++++++
 drivers/net/ethernet/sunplus/l2sw_desc.h           |  21 +
 drivers/net/ethernet/sunplus/l2sw_driver.c         | 779 +++++++++++++++++++++
 drivers/net/ethernet/sunplus/l2sw_driver.h         |  23 +
 drivers/net/ethernet/sunplus/l2sw_hal.c            | 422 +++++++++++
 drivers/net/ethernet/sunplus/l2sw_hal.h            |  47 ++
 drivers/net/ethernet/sunplus/l2sw_int.c            | 326 +++++++++
 drivers/net/ethernet/sunplus/l2sw_int.h            |  16 +
 drivers/net/ethernet/sunplus/l2sw_mac.c            |  68 ++
 drivers/net/ethernet/sunplus/l2sw_mac.h            |  24 +
 drivers/net/ethernet/sunplus/l2sw_mdio.c           | 118 ++++
 drivers/net/ethernet/sunplus/l2sw_mdio.h           |  19 +
 drivers/net/ethernet/sunplus/l2sw_register.h       |  99 +++
 20 files changed, 2575 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
 create mode 100644 drivers/net/ethernet/sunplus/Kconfig
 create mode 100644 drivers/net/ethernet/sunplus/Makefile
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_define.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_register.h
Signature · 1 line
2.7.4
author
#1 lore raw

PATCH 1/2 devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

Add bindings documentation for Sunplus SP7021.

---
 .../bindings/net/sunplus,sp7021-l2sw.yaml          | 123 +++++++++++++++++++++
 MAINTAINERS                                        |   7 ++
 2 files changed, 130 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml new file +123 -0
new file mode 100644
index 0000000..1fc253d
@@ -0,0 +1,123 @@
+# 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-l2sw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 Dual Ethernet MAC Device Tree Bindings
+
+maintainers:
+  - Wells Lu <[email protected]>
+
+description: |
+  Sunplus SP7021 dual 10M/100M Ethernet MAC controller with Layer 2 switch.
+  The controller can operate at either dual Ethernet MAC mode or one Ethernet
+  MAC with layer 2 switch (daisy-chain) mode.
+  The device node of Sunplus SP7021 Ethernet (L2SW) MAC controller has
+  following properties.
+
+properties:
+  compatible:
+    const: sunplus,sp7021-l2sw
+
+  reg:
+    items:
+      - description: Base address and length of the L2SW registers.
+      - description: Base address and length of the MOON5 registers.
+
+  reg-names:
+    items:
+      - const: l2sw
+      - const: moon5
+
+  interrupts:
+    description: |
+      Contains number and type of interrupt. Number should be 66.
+      Type should be high-level trigger
+    maxItems: 1
+
+  clocks:
+    description: |
+      Clock controller selector for Ethernet MAC controller.
+    maxItems: 1
+
+  resets:
+    description: |
+      Reset controller selector for Ethernet MAC controller.
+    maxItems: 1
+
+  phy-handle1:
+    description: A handle to node of phy 1 in mdio node
+    maxItems: 1
+
+  phy-handle2:
+    description: A handle to node of phy 2 in mdio node
+    maxItems: 1
+
+  pinctrl-names:
+    description: |
+      Names corresponding to the numbered pinctrl states.
+      A pinctrl state named "default" must be defined.
+    const: default
+
+  pinctrl-0:
+    description: A handle to the 'default' state of pin configuration
+
+  nvmem-cells:
+    items:
+      - description: nvmem cell address of MAC address of MAC 1
+      - description: nvmem cell address of MAC address of MAC 2
+
+  nvmem-cell-names:
+    description: names corresponding to the nvmem cells of MAC address
+    items:
+      - const: mac_addr0
+      - const: mac_addr1
+
+  mode:
+    description: |
+      Set operating modes of Sunplus Dual Ethernet MAC controller.
+      Please set one of the following modes:
+        0: daisy-chain mode
+        1: dual NIC mode
+        2: daisy-chain mode but disable SA learning
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [ 0, 1, 2 ]
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - clocks
+  - resets
+  - phy-handle1
+  - phy-handle2
+  - pinctrl-names
+  - pinctrl-0
+  - nvmem-cells
+  - nvmem-cell-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    l2sw: l2sw@9c108000 {
+        compatible = "sunplus,sp7021-l2sw";
+        reg = <0x9c108000 0x400>, <0x9c000280 0x80>;
+        reg-names = "l2sw", "moon5";
+        interrupt-parent = <&intc>;
+        interrupts = <66 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clkc 0xa7>;
+        resets = <&rstc 0x97>;
+        phy-handle1 = <&eth_phy0>;
+        phy-handle2 = <&eth_phy1>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&l2sw_demo_board_v3_pins>;
+        nvmem-cells = <&mac_addr0>, <&mac_addr1>;
+        nvmem-cell-names = "mac_addr0", "mac_addr1";
+        mode = < 1 >;
+    };
+...
MAINTAINERS +7 -0
index dcc1819..4669c16 100644
@@ -18000,6 +18000,13 @@ L:	[email protected]
 S:	Maintained
 F:	drivers/net/ethernet/dlink/sundance.c
 
+SUNPLUS ETHERNET DRIVER
+M:	Wells Lu <[email protected]>
+L:	[email protected]
+S:	Maintained
+W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
+
 SUPERH
 M:	Yoshinori Sato <[email protected]>
 M:	Rich Felker <[email protected]>
Signature · 1 line
2.7.4
author
#2 lore raw

PATCH 2/2 net: ethernet: Add driver for Sunplus SP7021

In reply to Wells Lu

Add Ethernet driver for Sunplus SP7021.

---
 MAINTAINERS                                  |   1 +
 drivers/net/ethernet/Kconfig                 |   1 +
 drivers/net/ethernet/Makefile                |   1 +
 drivers/net/ethernet/sunplus/Kconfig         |  20 +
 drivers/net/ethernet/sunplus/Makefile        |   6 +
 drivers/net/ethernet/sunplus/l2sw_define.h   | 221 ++++++++
 drivers/net/ethernet/sunplus/l2sw_desc.c     | 233 ++++++++
 drivers/net/ethernet/sunplus/l2sw_desc.h     |  21 +
 drivers/net/ethernet/sunplus/l2sw_driver.c   | 779 +++++++++++++++++++++++++++
 drivers/net/ethernet/sunplus/l2sw_driver.h   |  23 +
 drivers/net/ethernet/sunplus/l2sw_hal.c      | 422 +++++++++++++++
 drivers/net/ethernet/sunplus/l2sw_hal.h      |  47 ++
 drivers/net/ethernet/sunplus/l2sw_int.c      | 326 +++++++++++
 drivers/net/ethernet/sunplus/l2sw_int.h      |  16 +
 drivers/net/ethernet/sunplus/l2sw_mac.c      |  68 +++
 drivers/net/ethernet/sunplus/l2sw_mac.h      |  24 +
 drivers/net/ethernet/sunplus/l2sw_mdio.c     | 118 ++++
 drivers/net/ethernet/sunplus/l2sw_mdio.h     |  19 +
 drivers/net/ethernet/sunplus/l2sw_register.h |  99 ++++
 19 files changed, 2445 insertions(+)
 create mode 100644 drivers/net/ethernet/sunplus/Kconfig
 create mode 100644 drivers/net/ethernet/sunplus/Makefile
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_define.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.c
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.h
 create mode 100644 drivers/net/ethernet/sunplus/l2sw_register.h
MAINTAINERS +1 -0
index 4669c16..ca676ec 100644
@@ -18006,6 +18006,7 @@ L:	[email protected]
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
+F:	drivers/net/ethernet/sunplus/
 
 SUPERH
 M:	Yoshinori Sato <[email protected]>
drivers/net/ethernet/Kconfig +1 -0
index 412ae3e..0084852 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index aaa5078..e4ce162 100644
@@ -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/
drivers/net/ethernet/sunplus/Kconfig new file +20 -0
new file mode 100644
index 0000000..a9e3a4c
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration
+#
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	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 (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.
drivers/net/ethernet/sunplus/Makefile new file +6 -0
new file mode 100644
index 0000000..b401cec
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Sunplus network device drivers.
+#
+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o
+sp_l2sw-objs := l2sw_driver.o l2sw_int.o l2sw_hal.o l2sw_desc.o l2sw_mac.o l2sw_mdio.o
drivers/net/ethernet/sunplus/l2sw_define.h new file +221 -0

221 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_desc.c new file +233 -0

233 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_desc.h new file +21 -0
new file mode 100644
index 0000000..d0647cb
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_DESC_H__
+#define __L2SW_DESC_H__
+
+#include "l2sw_define.h"
+
+void rx_descs_flush(struct l2sw_common *comm);
+void tx_descs_clean(struct l2sw_common *comm);
+void rx_descs_clean(struct l2sw_common *comm);
+void descs_clean(struct l2sw_common *comm);
+void descs_free(struct l2sw_common *comm);
+u32 tx_descs_init(struct l2sw_common *comm);
+u32 rx_descs_init(struct l2sw_common *comm);
+u32 descs_alloc(struct l2sw_common *comm);
+u32 descs_init(struct l2sw_common *comm);
+
+#endif
drivers/net/ethernet/sunplus/l2sw_driver.c new file +779 -0

779 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_driver.h new file +23 -0
new file mode 100644
index 0000000..a076b33
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_DRIVER_H__
+#define __L2SW_DRIVER_H__
+
+#include "l2sw_define.h"
+#include "l2sw_register.h"
+#include "l2sw_hal.h"
+#include "l2sw_int.h"
+#include "l2sw_mdio.h"
+#include "l2sw_mac.h"
+#include "l2sw_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
drivers/net/ethernet/sunplus/l2sw_hal.c new file +422 -0

422 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_hal.h new file +47 -0

47 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_int.c new file +326 -0

326 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_int.h new file +16 -0
new file mode 100644
index 0000000..b459481
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_INT_H__
+#define __L2SW_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);
+int l2sw_get_irq(struct platform_device *pdev, struct l2sw_common *comm);
+int l2sw_request_irq(struct platform_device *pdev, struct l2sw_common *comm,
+		     struct net_device *net_dev);
+
+#endif
drivers/net/ethernet/sunplus/l2sw_mac.c new file +68 -0

68 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_mac.h new file +24 -0
new file mode 100644
index 0000000..a053130
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_MAC_H__
+#define __L2SW_MAC_H__
+
+#include "l2sw_define.h"
+#include "l2sw_hal.h"
+
+bool mac_init(struct l2sw_mac *mac);
+
+void mac_soft_reset(struct l2sw_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
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +118 -0

118 lines not on this page · raw message

drivers/net/ethernet/sunplus/l2sw_mdio.h new file +19 -0
new file mode 100644
index 0000000..698fc0b
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_MDIO_H__
+#define __L2SW_MDIO_H__
+
+#include "l2sw_define.h"
+#include "l2sw_hal.h"
+
+u32  mdio_init(struct platform_device *pdev, struct net_device *net_dev);
+void mdio_remove(struct net_device *net_dev);
+int  mac_phy_probe(struct net_device *netdev);
+void mac_phy_start(struct net_device *netdev);
+void mac_phy_stop(struct net_device *netdev);
+void mac_phy_remove(struct net_device *netdev);
+
+#endif
drivers/net/ethernet/sunplus/l2sw_register.h new file +99 -0

99 lines not on this page · raw message

Signature · 1 line
2.7.4
#3 lore raw

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

11/3/21 2:02 PM, Wells Lu пишет:

Quoted 306 lines

Add Ethernet driver for Sunplus SP7021.

---
  MAINTAINERS                                  |   1 +
  drivers/net/ethernet/Kconfig                 |   1 +
  drivers/net/ethernet/Makefile                |   1 +
  drivers/net/ethernet/sunplus/Kconfig         |  20 +
  drivers/net/ethernet/sunplus/Makefile        |   6 +
  drivers/net/ethernet/sunplus/l2sw_define.h   | 221 ++++++++
  drivers/net/ethernet/sunplus/l2sw_desc.c     | 233 ++++++++
  drivers/net/ethernet/sunplus/l2sw_desc.h     |  21 +
  drivers/net/ethernet/sunplus/l2sw_driver.c   | 779 +++++++++++++++++++++++++++
  drivers/net/ethernet/sunplus/l2sw_driver.h   |  23 +
  drivers/net/ethernet/sunplus/l2sw_hal.c      | 422 +++++++++++++++
  drivers/net/ethernet/sunplus/l2sw_hal.h      |  47 ++
  drivers/net/ethernet/sunplus/l2sw_int.c      | 326 +++++++++++
  drivers/net/ethernet/sunplus/l2sw_int.h      |  16 +
  drivers/net/ethernet/sunplus/l2sw_mac.c      |  68 +++
  drivers/net/ethernet/sunplus/l2sw_mac.h      |  24 +
  drivers/net/ethernet/sunplus/l2sw_mdio.c     | 118 ++++
  drivers/net/ethernet/sunplus/l2sw_mdio.h     |  19 +
  drivers/net/ethernet/sunplus/l2sw_register.h |  99 ++++
  19 files changed, 2445 insertions(+)

create mode 100644 drivers/net/ethernet/sunplus/Kconfig create mode 100644 drivers/net/ethernet/sunplus/Makefile create mode 100644 drivers/net/ethernet/sunplus/l2sw_define.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_register.h

MAINTAINERS +1 -0
index 4669c16..ca676ec 100644
@@ -18006,6 +18006,7 @@ L:	[email protected]
  S:	Maintained
  W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
  F:	Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
+F:	drivers/net/ethernet/sunplus/
 
  SUPERH
  M:	Yoshinori Sato <[email protected]>
drivers/net/ethernet/Kconfig +1 -0
index 412ae3e..0084852 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index aaa5078..e4ce162 100644
@@ -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/
drivers/net/ethernet/sunplus/Kconfig new file +20 -0
new file mode 100644
index 0000000..a9e3a4c
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration
+#
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	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 (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.
drivers/net/ethernet/sunplus/Makefile new file +6 -0
new file mode 100644
index 0000000..b401cec
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Sunplus network device drivers.
+#
+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o
+sp_l2sw-objs := l2sw_driver.o l2sw_int.o l2sw_hal.o l2sw_desc.o l2sw_mac.o l2sw_mdio.o
drivers/net/ethernet/sunplus/l2sw_define.h new file +185 -0
new file mode 100644
index 0000000..c1049c5
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_DEFINE_H__
+#define __L2SW_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>
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "[L2SW]" fmt
+
+//define MAC interrupt status bit
+#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)
+
+#define MAC_PHY_ADDR                    0x01	/* define by hardware */
+
+/*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 l2sw_common {
+	struct net_device *net_dev;
+	struct platform_device *pdev;
+	int dual_nic;
+	int sa_learning;
+
+	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;
+

drivers/net/ethernet/sunplus/l2sw_define.h line 195

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +10 -0
+	struct mii_bus *mii_bus;
+	struct phy_device *phy_dev;
+
+	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
+	struct mutex store_mode; // mutex for dynamic mode change

run checkpatch.pl and fix the errors

#4 lore raw

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

Hi,

Thanks a lot for review.

Quoted 334 lines

From: Denis Kirjanov <[email protected]>
Sent: Wednesday, November 3, 2021 8:05 PM
To: Wells Lu <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Cc: Wells Lu 呂芳騰 <[email protected]>
Subject: Re: [PATCH 2/2] net: ethernet: Add driver for Sunplus SP7021

11/3/21 2:02 PM, Wells Lu пишет:

Quoted 13 lines

Add Ethernet driver for Sunplus SP7021.

---
  MAINTAINERS                                  |   1 +
  drivers/net/ethernet/Kconfig                 |   1 +
  drivers/net/ethernet/Makefile                |   1 +
  drivers/net/ethernet/sunplus/Kconfig         |  20 +
  drivers/net/ethernet/sunplus/Makefile        |   6 +
  drivers/net/ethernet/sunplus/l2sw_define.h   | 221 ++++++++
  drivers/net/ethernet/sunplus/l2sw_desc.c     | 233 ++++++++
  drivers/net/ethernet/sunplus/l2sw_desc.h     |  21 +
  drivers/net/ethernet/sunplus/l2sw_driver.c   | 779

+++++++++++++++++++++++++++

Quoted 160 lines
  drivers/net/ethernet/sunplus/l2sw_driver.h   |  23 +
  drivers/net/ethernet/sunplus/l2sw_hal.c      | 422 +++++++++++++++
  drivers/net/ethernet/sunplus/l2sw_hal.h      |  47 ++
  drivers/net/ethernet/sunplus/l2sw_int.c      | 326 +++++++++++
  drivers/net/ethernet/sunplus/l2sw_int.h      |  16 +
  drivers/net/ethernet/sunplus/l2sw_mac.c      |  68 +++
  drivers/net/ethernet/sunplus/l2sw_mac.h      |  24 +
  drivers/net/ethernet/sunplus/l2sw_mdio.c     | 118 ++++
  drivers/net/ethernet/sunplus/l2sw_mdio.h     |  19 +
  drivers/net/ethernet/sunplus/l2sw_register.h |  99 ++++
  19 files changed, 2445 insertions(+)

create mode 100644 drivers/net/ethernet/sunplus/Kconfig create mode 100644 drivers/net/ethernet/sunplus/Makefile create mode 100644 drivers/net/ethernet/sunplus/l2sw_define.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.c create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.h create mode 100644 drivers/net/ethernet/sunplus/l2sw_register.h

MAINTAINERS +1 -0
@@ -18006,6 +18006,7 @@ L:	[email protected]
  S:	Maintained
  W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
  F:	Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml
+F:	drivers/net/ethernet/sunplus/
 
  SUPERH
  M:	Yoshinori Sato <[email protected]>
(quoted diff) +0 -0

b/drivers/net/ethernet/Kconfig index 412ae3e..0084852 100644

drivers/net/ethernet/Kconfig +1 -0
@@ -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"
(quoted diff) +0 -0

b/drivers/net/ethernet/Makefile index aaa5078..e4ce162 100644

drivers/net/ethernet/Makefile +1 -0
@@ -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..a9e3a4c

drivers/net/ethernet/sunplus/Kconfig new file +19 -0
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration #
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	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 (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.
drivers/net/ethernet/sunplus/Makefile new file +7 -0
new file mode 100644
index 0000000..b401cec
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Sunplus network device drivers.
+#
+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o sp_l2sw-objs :=
+l2sw_driver.o l2sw_int.o l2sw_hal.o l2sw_desc.o l2sw_mac.o
+l2sw_mdio.o
drivers/net/ethernet/sunplus/l2sw_define.h new file +50 -0
new file mode 100644
index 0000000..c1049c5
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __L2SW_DEFINE_H__
+#define __L2SW_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>
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "[L2SW]" fmt
+
+//define MAC interrupt status bit
+#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)

drivers/net/ethernet/sunplus/l2sw_define.h line 60

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +10 -0
+#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 | \

drivers/net/ethernet/sunplus/l2sw_define.h line 62

Quoted 2 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +2 -0
+					MAC_INT_RX_DES_ERR)
+#define MAC_INT_TX                      (MAC_INT_TX_DONE_L |

MAC_INT_TX_DONE_H | \

drivers/net/ethernet/sunplus/l2sw_define.h line 64

Quoted 2 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +2 -0
+					MAC_INT_TX_DES_ERR)
+#define MAC_INT_MASK_DEF

(MAC_INT_DAISY_MODE_CHG | MAC_INT_IP_CHKSUM_ERR | \

drivers/net/ethernet/sunplus/l2sw_define.h line 65

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_define.h new file +1 -0
+					MAC_INT_WDOG_TIMER1_EXP |

MAC_INT_WDOG_TIMER0_EXP | \

drivers/net/ethernet/sunplus/l2sw_define.h line 66

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_define.h new file +1 -0
+					MAC_INT_INTRUDER_ALERT | MAC_INT_BC_STORM

| \

drivers/net/ethernet/sunplus/l2sw_define.h line 67

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_define.h new file +1 -0
+					MAC_INT_MUST_DROP_LAN |

MAC_INT_GLOBAL_QUE_FULL | \

drivers/net/ethernet/sunplus/l2sw_define.h line 68

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_define.h new file +1 -0
+					MAC_INT_TX_SOC_PAUSE_ON |

MAC_INT_RX_SOC_QUE_FULL | \

drivers/net/ethernet/sunplus/l2sw_define.h line 69

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_define.h new file +1 -0
+					MAC_INT_TX_LAN1_QUE_FULL |

MAC_INT_TX_LAN0_QUE_FULL | \

Quoted 50 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +50 -0
+					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

drivers/net/ethernet/sunplus/l2sw_define.h

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +10 -0
+#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)
+
+#define MAC_PHY_ADDR                    0x01	/* define by

hardware */

Quoted 60 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +60 -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 l2sw_common {
+	struct net_device *net_dev;
+	struct platform_device *pdev;
+	int dual_nic;
+	int sa_learning;
+
+	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 phy_device *phy_dev;
+
+	struct napi_struct rx_napi;
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_define.h line 195

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_define.h new file +6 -0
+	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
+	struct mutex store_mode; // mutex for dynamic mode change

run checkpatch.pl and fix the errors

I ran checkpatch.pl with -f option for all *.c and *.h files, like:

   $ checkpatch.pl -f *.c
   $ checkpatch.pl -f *.h

I got 0 warnings and 0 errors for all files.

Also, I ran checkpath.pl for patch files, except cover-letter patch, as shown below:

   $ checkpatch.pl 0001*.patch
   $ checkpatch.pl 0002*.patch

I got 0 warnings and 0 errors for the 2 patch files. But the second patch file shows some 'CHECK' items, like

"Unnecessary parentheses around...", "Macro argument reuse 'N' may be better as'(N)'..."

I am sure the code has no problem.
Do I need to clean 'CHECK' items?

Best regards,

#5 lore raw

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

In reply to Wells Lu

On Wed, 2021-11-03 at 19:02 +0800, Wells Lu wrote:
[...]

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +0 -0
new file mode 100644
index 0000000..3dfd0dd
@@ -0,0 +1,779 @@

[...]

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+static int l2sw_probe(struct platform_device *pdev)
+{
+	struct l2sw_common *comm;
+	struct resource *r_mem;
+	struct net_device *net_dev, *net_dev2;

drivers/net/ethernet/sunplus/l2sw_driver.c line 512

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+	struct l2sw_mac *mac, *mac2;
+	u32 mode;
+	int ret = 0;
+	int rc;
+
+	if (platform_get_drvdata(pdev))
+		return -ENODEV;
+
+	// Allocate memory for l2sw 'common' area.
+	comm = kmalloc(sizeof(*comm), GFP_KERNEL);

I'd use devm_kzalloc() here for initialization and to simplify the cleanup path.

drivers/net/ethernet/sunplus/l2sw_driver.c line 515

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +3 -0
+	if (!comm)
+		return -ENOMEM;
+	pr_debug(" comm = %p\n", comm);

What is this useful for?

drivers/net/ethernet/sunplus/l2sw_driver.c line 516

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+	memset(comm, '\0', sizeof(struct l2sw_common));

Not needed with kzalloc, See above.

[...]

Quoted 21 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +21 -0
+	// Get memory resoruce 0 from dts.
+	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r_mem) {
+		pr_debug(" res->name = \"%s\", r_mem->start = %pa\n", r_mem->name, &r_mem->start);
+		if (l2sw_reg_base_set(devm_ioremap(&pdev->dev, r_mem->start,
+						   (r_mem->end - r_mem->start + 1))) != 0) {
+			pr_err(" ioremap failed!\n");
+			ret = -ENOMEM;
+			goto out_free_comm;
+		}
+	} else {
+		pr_err(" No MEM resource 0 found!\n");
+		ret = -ENXIO;
+		goto out_free_comm;
+	}
+
+	// Get memory resoruce 1 from dts.
+	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (r_mem) {
+		pr_debug(" res->name = \"%s\", r_mem->start = %pa\n", r_mem->name, &r_mem->start);
+		if (moon5_reg_base_set(devm_ioremap(&pdev->dev, r_mem->start,

drivers/net/ethernet/sunplus/l2sw_driver.c line 554

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+						    (r_mem->end - r_mem->start + 1))) != 0) {
+			pr_err(" ioremap failed!\n");
+			ret = -ENOMEM;
+			goto out_free_comm;
+		}
+	} else {
+		pr_err(" No MEM resource 1 found!\n");
+		ret = -ENXIO;
+		goto out_free_comm;
+	}

Using devm_ioremap_resource() would simplify both a lot.

[...]

drivers/net/ethernet/sunplus/l2sw_driver.c line 586

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+	comm->rstc = devm_reset_control_get(&pdev->dev, NULL);

Please use devm_reset_control_get_exclusive().

drivers/net/ethernet/sunplus/l2sw_driver.c line 597

Quoted 11 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +11 -0
+	if (IS_ERR(comm->rstc)) {
+		dev_err(&pdev->dev, "Failed to retrieve reset controller!\n");
+		ret = PTR_ERR(comm->rstc);
+		goto out_free_comm;
+	}
+
+	// Enable clock.
+	clk_prepare_enable(comm->clk);
+	udelay(1);
+
+	ret = reset_control_assert(comm->rstc);

No need to assign to ret if you ignore it anyway.

drivers/net/ethernet/sunplus/l2sw_driver.c line 605

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+	udelay(1);
+	ret = reset_control_deassert(comm->rstc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to deassert reset line (err = %d)!\n", ret);
+		ret = -ENODEV;
+		goto out_free_comm;
+	}
+	udelay(1);

regards
Philipp

#6 lore raw

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

Thanks a lot for your review.

Quoted 27 lines

From: Philipp Zabel <[email protected]>
Sent: Wednesday, November 3, 2021 8:10 PM
To: Wells Lu <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]
Cc: Wells Lu 呂芳騰 <[email protected]>
Subject: Re: [PATCH 2/2] net: ethernet: Add driver for Sunplus SP7021

On Wed, 2021-11-03 at 19:02 +0800, Wells Lu wrote:
[...]

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +0 -0
new file mode 100644
index 0000000..3dfd0dd
@@ -0,0 +1,779 @@

[...]

Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +9 -0
+static int l2sw_probe(struct platform_device *pdev) {
+	struct l2sw_common *comm;
+	struct resource *r_mem;
+	struct net_device *net_dev, *net_dev2;
+	struct l2sw_mac *mac, *mac2;
+	u32 mode;
+	int ret = 0;
+	int rc;
+
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 512

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+	if (platform_get_drvdata(pdev))
+		return -ENODEV;
+
+	// Allocate memory for l2sw 'common' area.
+	comm = kmalloc(sizeof(*comm), GFP_KERNEL);

I'd use devm_kzalloc() here for initialization and to simplify the cleanup path.

I'll replace kmalloc() with devm_kzalloc() and remove the kfree(...) in cleanup path (platform remove) in PATCH v2.

Quoted 5 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 515

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +3 -0
+	if (!comm)
+		return -ENOMEM;
+	pr_debug(" comm = %p\n", comm);

What is this useful for?

'pr_debug(" comm = %p\n", comm);' is for early-stage debug. I'll remove the line in PATCH v2.

Quoted 3 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 516

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+	memset(comm, '\0', sizeof(struct l2sw_common));

Not needed with kzalloc, See above.

Yes, I'll remove the line 'memset(...);' as devm_kzalloc() will be used in PATCH v2.

Quoted 25 lines

[...]

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +4 -0
+	// Get memory resoruce 0 from dts.
+	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r_mem) {
+		pr_debug(" res->name = \"%s\", r_mem->start = %pa\n",

r_mem->name, &r_mem->start);

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +6 -0
+		if (l2sw_reg_base_set(devm_ioremap(&pdev->dev, r_mem->start,
+						   (r_mem->end - r_mem->start + 1))) != 0) {
+			pr_err(" ioremap failed!\n");
+			ret = -ENOMEM;
+			goto out_free_comm;
+		}

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+	} else {
+		pr_err(" No MEM resource 0 found!\n");
+		ret = -ENXIO;
+		goto out_free_comm;
+	}
+
+	// Get memory resoruce 1 from dts.
+	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (r_mem) {
+		pr_debug(" res->name = \"%s\", r_mem->start = %pa\n",

r_mem->name, &r_mem->start);

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+		if (moon5_reg_base_set(devm_ioremap(&pdev->dev,

r_mem->start,

Quoted 12 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 554

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+						    (r_mem->end - r_mem->start + 1))) != 0) {
+			pr_err(" ioremap failed!\n");
+			ret = -ENOMEM;
+			goto out_free_comm;
+		}
+	} else {
+		pr_err(" No MEM resource 1 found!\n");
+		ret = -ENXIO;
+		goto out_free_comm;
+	}

Using devm_ioremap_resource() would simplify both a lot.

Yes, I'll replace devm_ioremap() with devm_ioremap_resource() in PATCH v2.

Quoted 4 lines

[...]

drivers/net/ethernet/sunplus/l2sw_driver.c line 586

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+	comm->rstc = devm_reset_control_get(&pdev->dev, NULL);

Please use devm_reset_control_get_exclusive().

Yes, I'll replace devm_reset_control_get() with devm_reset_control_get_exclusive() in PATCH v2.

Quoted 5 lines
Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+	if (IS_ERR(comm->rstc)) {
+		dev_err(&pdev->dev, "Failed to retrieve reset controller!\n");
+		ret = PTR_ERR(comm->rstc);
+		goto out_free_comm;
+	}
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 597

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +6 -0
+
+	// Enable clock.
+	clk_prepare_enable(comm->clk);
+	udelay(1);
+
+	ret = reset_control_assert(comm->rstc);

No need to assign to ret if you ignore it anyway.

Ok, I'll remove 'ret =' in PATCH v2

Quoted 5 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +4 -0
+	udelay(1);
+	ret = reset_control_deassert(comm->rstc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to deassert reset line (err = %d)!\n",

ret);

Quoted 7 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 605

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +4 -0
+		ret = -ENODEV;
+		goto out_free_comm;
+	}
+	udelay(1);

regards
Philipp

Best regards,
Wells

#7 lore raw

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

In reply to Wells Lu

Hi--

Quoted 16 lines · Wells Lu
drivers/net/ethernet/sunplus/Kconfig new file +10 -0
new file mode 100644
index 0000000..a9e3a4c
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration
+#
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	select PHYLIB
+	select PINCTRL_SPPCTL

drivers/net/ethernet/sunplus/Kconfig line 20

Quoted 10 lines
drivers/net/ethernet/sunplus/Kconfig new file +10 -0
+	select COMMON_CLK_SP7021
+	select RESET_SUNPLUS
+	select NVMEM_SUNPLUS_OCOTP
+	help
+	  If you have Sunplus dual 10M/100M Ethernet (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.

Please use NET_VENDOR_SUNPLUS in the same way that other NET_VENDOR_wyxz kconfig symbols are used. It should just enable or disable any specific device drivers under it.

Signature · 1 line
~Randy
#8 lore raw

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

Quoted 36 lines

Hi--

Quoted 16 lines · Wells Lu
drivers/net/ethernet/sunplus/Kconfig new file +9 -0
new file mode 100644
index 0000000..a9e3a4c
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration #
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	select PHYLIB
+	select PINCTRL_SPPCTL

drivers/net/ethernet/sunplus/Kconfig line 19

Quoted 10 lines
drivers/net/ethernet/sunplus/Kconfig new file +10 -0
+	select COMMON_CLK_SP7021
+	select RESET_SUNPLUS
+	select NVMEM_SUNPLUS_OCOTP
+	help
+	  If you have Sunplus dual 10M/100M Ethernet (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.

Please use NET_VENDOR_SUNPLUS in the same way that other NET_VENDOR_wyxz kconfig symbols are used. It should just enable or disable any specific device drivers under it.

Quoted 2 lines

--
~Randy

I looked up Kconfig file of other vendors, but not sure what I should do. Do I need to modify Kconfig file in the form as shown below?

# SPDX-License-Identifier: GPL-2.0
#
# Sunplus device configuration
#

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS
---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 (with L2 switch) devices" depends on ETHERNET && SOC_SP7021
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 (with L2 switch) devices, say Y.
The network device supports dual 10M/100M Ethernet interfaces, or one 10/100M Ethernet interface with two LAN ports. To compile this driver as a module, choose M here. The module will be called sp_l2sw.

endif # NET_VENDOR_SUNPLUS

Best regards,
Wells

#9 lore raw

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

Quoted 4 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

Quoted 10 lines

---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.

Quoted 5 lines

if NET_VENDOR_SUNPLUS

config SP7021_EMAC

	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
	depends on ETHERNET && SOC_SP7021

Does it actually depend on SOC_SP7021 to build?

Andrew

#10 lore raw

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

Hi,

Thanks a lot for review.

Quoted 7 lines
Quoted 5 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

ARCH_SUNPLUS will be defined for Sunplus family series SoC. Ethernet devices of Sunplus are designed and used for Sunplus SoC. So far, only two SoC of Sunplus have the network device. I'd like to show up the selection only for Sunplus SoC.

Quoted 3 lines

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

I am not sure if this is mandatory or not.
Should I add COMPILE_TEST as below?

depends on ARCH_SUNPLUS | COMPILE_TEST

Quoted 13 lines
Quoted 13 lines

---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

Quoted 7 lines
Quoted 3 lines

config SP7021_EMAC

	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
	depends on ETHERNET && SOC_SP7021

Does it actually depend on SOC_SP7021 to build?

Andrew

Yes, the device is now only for Sunplus SP7021 SoC. Devices in each SoC may have a bit difference because of adding new function or improving something.

#11 lore raw

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

Quoted 10 lines · Wells Lu 呂芳騰

Hi,

Thanks a lot for review.

Quoted 6 lines
Quoted 5 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Quoted 6 lines
Quoted 1 line

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

ARCH_SUNPLUS will be defined for Sunplus family series SoC. Ethernet devices of Sunplus are designed and used for Sunplus SoC. So far, only two SoC of Sunplus have the network device. I'd like to show up the selection only for Sunplus SoC.

So it does not actually depend on ARCH_SUNPLUS. There are a few cases where drivers have needed to call into arch specific code, which stops them building for any other arch.

Quoted 7 lines
Quoted 2 lines

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

I am not sure if this is mandatory or not.
Should I add COMPILE_TEST as below?

depends on ARCH_SUNPLUS | COMPILE_TEST

Yes.

Quoted 3 lines

Yes, the device is now only for Sunplus SP7021 SoC. Devices in each SoC may have a bit difference because of adding new function or improving something.

If it will compile with COMPILE_TEST on x86, mips, etc, you should allow it to compile with COMPILE_TEST. You get better compile testing that way.

Andrew

#12 lore raw

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

Quoted 25 lines · Andrew Lunn
Quoted 10 lines · Wells Lu 呂芳騰

Hi,

Thanks a lot for review.

Quoted 6 lines
Quoted 5 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Quoted 6 lines
Quoted 1 line

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

ARCH_SUNPLUS will be defined for Sunplus family series SoC. Ethernet devices of Sunplus are designed and used for Sunplus SoC. So far, only two SoC of Sunplus have the network device. I'd like to show up the selection only for Sunplus SoC.

So it does not actually depend on ARCH_SUNPLUS. There are a few cases where drivers have needed to call into arch specific code, which stops them building for any other arch.

Quoted 3 lines
Quoted 2 lines

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

Quoted 6 lines
Quoted 4 lines

I am not sure if this is mandatory or not.
Should I add COMPILE_TEST as below?

depends on ARCH_SUNPLUS | COMPILE_TEST

Yes.

Yes, but use "||" instead of one "|".

Quoted 11 lines
Quoted 4 lines

Yes, the device is now only for Sunplus SP7021 SoC. Devices in each SoC may have a bit difference because of adding new function or improving something.

If it will compile with COMPILE_TEST on x86, mips, etc, you should allow it to compile with COMPILE_TEST. You get better compile testing that way.

Andrew

Signature · 1 line
~Randy
#13 lore raw

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

Quoted 44 lines · Andrew Lunn
Quoted 25 lines · Wells Lu 呂芳騰
Quoted 10 lines

Hi,

Thanks a lot for review.

Quoted 6 lines
Quoted 5 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Quoted 6 lines
Quoted 1 line

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

ARCH_SUNPLUS will be defined for Sunplus family series SoC. Ethernet devices of Sunplus are designed and used for Sunplus SoC. So far, only two SoC of Sunplus have the network device. I'd like to show up the selection only for Sunplus SoC.

So it does not actually depend on ARCH_SUNPLUS. There are a few cases where drivers have needed to call into arch specific code, which stops them building for any other arch.

Quoted 3 lines
Quoted 2 lines

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

Quoted 6 lines
Quoted 4 lines

I am not sure if this is mandatory or not.
Should I add COMPILE_TEST as below?

depends on ARCH_SUNPLUS | COMPILE_TEST

Yes.

Yes, but use "||" instead of one "|".

Quoted 9 lines
Quoted 4 lines

Yes, the device is now only for Sunplus SP7021 SoC. Devices in each SoC may have a bit difference because of adding new function or improving something.

If it will compile with COMPILE_TEST on x86, mips, etc, you should allow it to compile with COMPILE_TEST. You get better compile testing that way.

Quoted 6 lines
Quoted 2 lines

Andrew

--
~Randy

I will do it in PATCH v2.

Thanks,

#14 lore raw

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

In reply to Andrew Lunn
Quoted 32 lines · Wells Lu 呂芳騰
Quoted 10 lines

Hi,

Thanks a lot for review.

Quoted 6 lines
Quoted 5 lines

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS

Quoted 6 lines
Quoted 1 line

Does it actually depend on ARCH_SUNPLUS? What do you make use of?

ARCH_SUNPLUS will be defined for Sunplus family series SoC. Ethernet devices of Sunplus are designed and used for Sunplus SoC. So far, only two SoC of Sunplus have the network device. I'd like to show up the selection only for Sunplus SoC.

So it does not actually depend on ARCH_SUNPLUS. There are a few cases where drivers have needed to call into arch specific code, which stops them building for any other arch.

Quoted 7 lines
Quoted 2 lines

Ideally, you want it to also build with COMPILE_TEST, so that the driver gets build by 0-day and all the other build bots.

I am not sure if this is mandatory or not.
Should I add COMPILE_TEST as below?

depends on ARCH_SUNPLUS | COMPILE_TEST

Yes.

Quoted 8 lines
Quoted 3 lines

Yes, the device is now only for Sunplus SP7021 SoC. Devices in each SoC may have a bit difference because of adding new function or improving something.

If it will compile with COMPILE_TEST on x86, mips, etc, you should allow it to compile with COMPILE_TEST. You get better compile testing that way.

Andrew

No, we only develop arm-based SoC, never for x86 or mips. We never compile the driver for x86 or mips machine.

#15 lore raw

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

Quoted 2 lines

No, we only develop arm-based SoC, never for x86 or mips. We never compile the driver for x86 or mips machine.

You don't, but the Linux community does build for those architectures. Most people do tree wide refactoring work using x86. Tree wide cleanups using x86, etc. Any changes like that could touch your driver. The harder is it to build, the less build testing it will get, and tree wide changes which break it are less likely to get noticed. So you really do want it to compile cleanly for all architectures. If it does not, it normally actually means you are doing something wrong, something you need to fix anyway. So please do build it for x86 and make sure it builds cleanly.

Andrew

#16 lore raw

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

Quoted 12 lines
Quoted 2 lines

No, we only develop arm-based SoC, never for x86 or mips. We never compile the driver for x86 or mips machine.

You don't, but the Linux community does build for those architectures. Most people do tree wide refactoring work using x86. Tree wide cleanups using x86, etc. Any changes like that could touch your driver. The harder is it to build, the less build testing it will get, and tree wide changes which break it are less likely to get noticed. So you really do want it to compile cleanly for all architectures. If it does not, it normally actually means you are doing something wrong, something you need to fix anyway. So please do build it for x86 and make sure it builds cleanly.

Quoted 1 line

Andrew

Ok, I understand.
I'll add COMPILE_TEST and compile driver for x86.

Thanks,

#17 lore raw

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

In reply to Wells Lu 呂芳騰
Quoted 36 lines · Wells Lu 呂芳騰
Quoted 36 lines

Hi--

Quoted 16 lines · Wells Lu
drivers/net/ethernet/sunplus/Kconfig new file +9 -0
new file mode 100644
index 0000000..a9e3a4c
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sunplus Ethernet device configuration #
+
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"
+	depends on ETHERNET && SOC_SP7021
+	select PHYLIB
+	select PINCTRL_SPPCTL

drivers/net/ethernet/sunplus/Kconfig line 19

Quoted 10 lines
drivers/net/ethernet/sunplus/Kconfig new file +10 -0
+	select COMMON_CLK_SP7021
+	select RESET_SUNPLUS
+	select NVMEM_SUNPLUS_OCOTP
+	help
+	  If you have Sunplus dual 10M/100M Ethernet (with L2 switch)
+	  devices, say Y.
+	  The network device supports dual 10M/100M Ethernet interfaces,
+	  or one 10/100M Ethernet interface with two LAN ports.
+	  To compile this driver as a module, choose M here.  The module
+	  will be called sp_l2sw.

Please use NET_VENDOR_SUNPLUS in the same way that other NET_VENDOR_wyxz kconfig symbols are used. It should just enable or disable any specific device drivers under it.

Quoted 5 lines
Quoted 2 lines

--
~Randy

I looked up Kconfig file of other vendors, but not sure what I should do. Do I need to modify Kconfig file in the form as shown below?

Hi,

Yes, this is the correct general idea, but also consider Andrew's comments.

Thanks.

Quoted 42 lines

# SPDX-License-Identifier: GPL-2.0
#
# Sunplus device configuration
#

config NET_VENDOR_SUNPLUS
bool "Sunplus devices"
default y
depends on ARCH_SUNPLUS
---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 (with L2 switch) devices" depends on ETHERNET && SOC_SP7021
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 (with L2 switch) devices, say Y.
The network device supports dual 10M/100M Ethernet interfaces, or one 10/100M Ethernet interface with two LAN ports. To compile this driver as a module, choose M here. The module will be called sp_l2sw.

endif # NET_VENDOR_SUNPLUS

Best regards,
Wells

Signature · 1 line
~Randy
#18 lore raw

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

In reply to Wells Lu

drivers/net/ethernet/sunplus/Kconfig line 7

Quoted 2 lines
drivers/net/ethernet/sunplus/Kconfig new file +2 -0
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"

The "with L2 Switch" is causing lots of warning bells to ring for me.

I don't see any references to switchdev or DSA in this driver. How is the switch managed? There have been a few examples in the past of similar two port switches being first supported in Dual MAC mode. Later trying to actually use the switch in the Linux was always ran into problems, and basically needed a new driver. So i want to make sure you don't have this problem.

In the Linux world, Ethernet switches default to having there ports/interfaces separated. This effectively gives you your dual MAC mode by default. You then create a Linux bridge, and add the ports/interfaces to the bridge. switchdev is used to offload the bridge, telling the hardware to enable the L2 switch between the ports.

So you don't need the mode parameter in DT. switchdev tells you this. Switchdev gives user space access to the address table etc.

Quoted 1 line

+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o

...

Quoted 1 line

+struct l2sw_common {

Please change your prefix. l2sw is a common prefix, there are other silicon vendors using l2sw. I would suggest sp_l2sw or spl2sw.

drivers/net/ethernet/sunplus/l2sw_driver.c line 171

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+static int ethernet_do_ioctl(struct net_device *net_dev, struct ifreq *ifr, int cmd)
+{
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+	struct l2sw_common *comm = mac->comm;
+	struct mii_ioctl_data *data = if_mii(ifr);
+	unsigned long flags;
+
+	pr_debug(" if = %s, cmd = %04x\n", ifr->ifr_ifrn.ifrn_name, cmd);
+	pr_debug(" phy_id = %d, reg_num = %d, val_in = %04x\n", data->phy_id,
+		 data->reg_num, data->val_in);

You should not be using any of the pr_ functions. You have a net_dev, so netdev_dbg().

drivers/net/ethernet/sunplus/l2sw_driver.c line 176

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+
+	// Check parameters' range.
+	if ((cmd == SIOCGMIIREG) || (cmd == SIOCSMIIREG)) {
+		if (data->reg_num > 31) {
+			pr_err(" reg_num (= %d) excesses range!\n", (int)data->reg_num);

Don't spam the kernel log for things like this.

drivers/net/ethernet/sunplus/l2sw_driver.c line 183

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +7 -0
+			return -EINVAL;
+		}
+	}
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1") == 0))

You cannot rely on the name, systemd has probably renamed it. If you have using phylib correctly, net_dev->phydev is what you want.

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +7 -0
+			return comm->phy2_addr;
+		else
+			return comm->phy1_addr;
+
+	case SIOCGMIIREG:
+		spin_lock_irqsave(&comm->ioctl_lock, flags);
+		data->val_out = mdio_read(data->phy_id, data->reg_num);

drivers/net/ethernet/sunplus/l2sw_driver.c line 200

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+		spin_unlock_irqrestore(&comm->ioctl_lock, flags);
+		pr_debug(" val_out = %04x\n", data->val_out);
+		break;
+
+	case SIOCSMIIREG:
+		spin_lock_irqsave(&comm->ioctl_lock, flags);
+		mdio_write(data->phy_id, data->reg_num, data->val_in);
+		spin_unlock_irqrestore(&comm->ioctl_lock, flags);
+		break;
+

You should be using phylink_mii_ioctl() or phy_mii_ioctl().

You locking is also suspect.

drivers/net/ethernet/sunplus/l2sw_driver.c line 214

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +7 -0
+static int ethernet_change_mtu(struct net_device *ndev, int new_mtu)
+{
+	if (netif_running(ndev))
+		return -EBUSY;
+
+	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
+		return -EINVAL;

The core will do this for you, if you set the values in the ndev correct at probe time.

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +4 -0
+
+	ndev->mtu = new_mtu;
+
+	return 0;

drivers/net/ethernet/sunplus/l2sw_hal.c line 329

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +8 -0
+}
+
 
 
+static int mdio_access(u8 op_cd, u8 dev_reg_addr, u8 phy_addr, u32 wdata)
+{
+	u32 value, time = 0;
+
+	HWREG_W(phy_cntl_reg0, (wdata << 16) | (op_cd << 13) | (dev_reg_addr << 8) | phy_addr);
+	wmb();			// make sure settings are effective.

That suggests you are using the wrong macros to access the registers.

drivers/net/ethernet/sunplus/l2sw_hal.c line 337

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +8 -0
+	do {
+		if (++time > MDIO_RW_TIMEOUT_RETRY_NUMBERS) {
+			pr_err(" mdio failed to operate!\n");
+			time = 0;
+		}
+
+		value = HWREG_R(phy_cntl_reg1);
+	} while ((value & 0x3) == 0);

include/linux/iopoll.h.

drivers/net/ethernet/sunplus/l2sw_hal.c line 340

Quoted 2 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +2 -0
+	if (time == 0)
+		return -1;

-ETIMDEOUT. One of the advantages of iopoll.h is that reusing code
avoids issues like this.

drivers/net/ethernet/sunplus/l2sw_hal.c line 347

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +3 -0
+u32 mdio_read(u32 phy_id, u16 regnum)
+{
+	int ret;

Please check for C45 and return -EOPNOTSUPP.

drivers/net/ethernet/sunplus/l2sw_hal.c line 359

Quoted 12 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +12 -0
+
+	ret = mdio_access(MDIO_READ_CMD, regnum, phy_id, 0);
+	if (ret < 0)
+		return -EIO;
+
+	return ret;
+}
+
+u32 mdio_write(u32 phy_id, u32 regnum, u16 val)
+{
+	int ret;
+

Please check for C45 and return -EOPNOTSUPP.

drivers/net/ethernet/sunplus/l2sw_hal.c line 367

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +8 -0
+	ret = mdio_access(MDIO_WRITE_CMD, regnum, phy_id, val);
+	if (ret < 0)
+		return -EIO;
+
+	return 0;
+}
+
+inline void tx_trigger(void)

No inline functions in C code. Let the compiler decide.

drivers/net/ethernet/sunplus/l2sw_hal.c line 419

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +7 -0
+int phy_cfg(struct l2sw_mac *mac)
+{
+	// Bug workaround:
+	// Flow-control of phy should be enabled. L2SW IP flow-control will refer
+	// to the bit to decide to enable or disable flow-control.
+	mdio_write(mac->comm->phy1_addr, 4, mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));
+	mdio_write(mac->comm->phy2_addr, 4, mdio_read(mac->comm->phy2_addr, 4) | (1 << 10));

This should be in the PHY driver. The MAC driver should never need to touch PHY registers.

Quoted 51 lines

+++ b/drivers/net/ethernet/sunplus/l2sw_mdio.c

drivers/net/ethernet/sunplus/l2sw_mdio.c new file +49 -0
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include "l2sw_mdio.h"
+
+static int mii_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	return mdio_read(phy_id, regnum);
+}
+
+static int mii_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+	return mdio_write(phy_id, regnum, val);
+}
+
+u32 mdio_init(struct platform_device *pdev, struct net_device *net_dev)
+{
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+	struct mii_bus *mii_bus;
+	struct device_node *mdio_node;
+	u32 ret;
+
+	mii_bus = mdiobus_alloc();
+	if (!mii_bus) {
+		pr_err(" 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->comm->phy1_node);
+	ret = of_mdiobus_register(mii_bus, mdio_node);
+	if (ret) {
+		pr_err(" Failed to register mii bus (ret = %d)!\n", ret);
+		mdiobus_free(mii_bus);
+		return ret;
+	}
+
+	mac->comm->mii_bus = mii_bus;
+	return ret;
+}
+

drivers/net/ethernet/sunplus/l2sw_mdio.c line 59

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +10 -0
+void mdio_remove(struct net_device *net_dev)
+{
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+
+	if (mac->comm->mii_bus) {
+		mdiobus_unregister(mac->comm->mii_bus);
+		mdiobus_free(mac->comm->mii_bus);
+		mac->comm->mii_bus = NULL;
+	}
+}

You MDIO code is pretty scattered around. Please bring it all together in one file.

drivers/net/ethernet/sunplus/l2sw_mdio.c line 63

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +3 -0
+static void mii_linkchange(struct net_device *netdev)
+{
+}

Nothing to do? Seems very odd. Don't you need to tell the MAC it should do 10Mbps or 100Mbps? What about pause?

drivers/net/ethernet/sunplus/l2sw_mdio.c line 72

Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +9 -0
+
+int mac_phy_probe(struct net_device *netdev)
+{
+	struct l2sw_mac *mac = netdev_priv(netdev);
+	struct phy_device *phydev;
+	int i;
+
+	phydev = of_phy_connect(mac->net_dev, mac->comm->phy1_node, mii_linkchange,
+				0, PHY_INTERFACE_MODE_RGMII_ID);

You should not hard code PHY_INTERFACE_MODE_RGMII_ID. Use the DT property "phy-mode"

drivers/net/ethernet/sunplus/l2sw_mdio.c line 75

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +3 -0
+	if (!phydev) {
+		pr_err(" \"%s\" has no phy found\n", netdev->name);
+		return -1;

-ENODEV;

Never use -1, pick an error code.

drivers/net/ethernet/sunplus/l2sw_mdio.c line 84

Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +9 -0
+	}
+
+	if (mac->comm->phy2_node) {
+		of_phy_connect(mac->net_dev, mac->comm->phy2_node, mii_linkchange,
+			       0, PHY_INTERFACE_MODE_RGMII_ID);
+	}
+
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);

So the MAC does not support pause? I'm then confused about phy_cfg().

Andrew

#19 lore raw

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

Hi,

Thanks a lot for your review.

Quoted 12 lines

drivers/net/ethernet/sunplus/Kconfig line 7

Quoted 2 lines
drivers/net/ethernet/sunplus/Kconfig new file +2 -0
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"

The "with L2 Switch" is causing lots of warning bells to ring for me.

I don't see any references to switchdev or DSA in this driver. How is the switch managed? There have been a few examples in the past of similar two port switches being first supported in Dual MAC mode. Later trying to actually use the switch in the Linux was always ran into problems, and basically needed a new driver. So i want to make sure you don't have this problem.

Quoted 8 lines

In the Linux world, Ethernet switches default to having there ports/interfaces separated. This effectively gives you your dual MAC mode by default. You then create a Linux bridge, and add the ports/interfaces to the bridge. switchdev is used to offload the bridge, telling the hardware to enable the L2 switch between the ports.

So you don't need the mode parameter in DT. switchdev tells you this. Switchdev gives user space access to the address table etc.

The L2 switch of Ethernet of SP7021 is not used to forward packets between two network interfaces.

Sunplus Dual Ethernet devices consists of one CPU port, two LAN ports, and a L2 switch. L2 switch is a circuitry which receives packets from CPU or LAN ports and then forwards them other ports. Rules of forwarding packets are set by driver.

Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
- Dual NIC mode
- An NIC with two LAN ports mode (daisy-chain mode)
- An NIC with two LAN ports mode 2

Dual NIC mode
Ethernet driver creates two net-device interfaces (eg: eth0 and eth1). Each has its dedicated LAN port. For example, LAN port 0 is for net-device interface eth0. LAN port 1 is for net-device interface eth1. Packets from LAN port 0 will be always forwarded to eth0 and vice versa by L2 switch. Similarly, packets from LAN port 1 will be always forwarded to eth1 and vice versa by L2 switch. Packets will never be forwarded between two LAN ports, or between eth0 and LAN port 1, or between eth1 and LAN port 0. The two network devices work independently.

An NIC with two LAN ports mode (daisy-chain mode) Ethernet driver creates one net-device interface (eg: eth0), but the net-device interface has two LAN ports. In this mode, a packet from one LAN port will be either forwarded to net-device interface (eht0) if its destination address matches MAC address of net-device interface (eth0), or forwarded to other LAN port. A packet from net-device interface (eth0) will be forwarded to a LAN port if its destination address is learnt by L2 switch, or forwarded to both LAN ports if its destination has not been learnt yet.

An NIC with two LAN ports mode 2
This mode is similar to “An NIC with two LAN ports mode”. The difference is that a packet from net-device interface (eth0) will be always forwarded to both LAN ports. Learning function of L2 switch is turned off in this mode. This means L2 switch will never learn the source address of a packet. So, it always forward packets to both LAN ports. This mode works like you have 2-port Ethernet hub.

Quoted 7 lines
Quoted 2 lines

+obj-$(CONFIG_NET_VENDOR_SUNPLUS) += sp_l2sw.o

...

Quoted 1 line

+struct l2sw_common {

Please change your prefix. l2sw is a common prefix, there are other silicon vendors using l2sw. I would suggest sp_l2sw or spl2sw.

Ok, I'll modify two struct names in next patch as shown below:
l2sw_common --> sp_common
l2sw_mac --> sp_mac

Should I also modify prefix of file name?

Quoted 10 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +9 -0
+static int ethernet_do_ioctl(struct net_device *net_dev, struct ifreq
+*ifr, int cmd) {
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+	struct l2sw_common *comm = mac->comm;
+	struct mii_ioctl_data *data = if_mii(ifr);
+	unsigned long flags;
+
+	pr_debug(" if = %s, cmd = %04x\n", ifr->ifr_ifrn.ifrn_name, cmd);
+	pr_debug(" phy_id = %d, reg_num = %d, val_in = %04x\n",

data->phy_id,

Quoted 4 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 171

Quoted 1 line
drivers/net/ethernet/sunplus/l2sw_driver.c new file +1 -0
+		 data->reg_num, data->val_in);

You should not be using any of the pr_ functions. You have a net_dev, so netdev_dbg().

Ok, I will replace pr_xxx() functions with netdev_xxx() functions where 'net_dev' is available in next patch.

Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+
+	// Check parameters' range.
+	if ((cmd == SIOCGMIIREG) || (cmd == SIOCSMIIREG)) {
+		if (data->reg_num > 31) {
+			pr_err(" reg_num (= %d) excesses range!\n",

(int)data->reg_num);

Don't spam the kernel log for things like this.

Ok, I'll remove the pr_err() line in next patch.

Quoted 11 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+			return -EINVAL;
+		}
+	}
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1") ==
+0))

You cannot rely on the name, systemd has probably renamed it. If you have using phylib correctly, net_dev->phydev is what you want.

Ok, I'll use name of the second net device to do compare, instead of using fixed string "eth1", in next patch.

Quoted 22 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +7 -0
+			return comm->phy2_addr;
+		else
+			return comm->phy1_addr;
+
+	case SIOCGMIIREG:
+		spin_lock_irqsave(&comm->ioctl_lock, flags);
+		data->val_out = mdio_read(data->phy_id, data->reg_num);

drivers/net/ethernet/sunplus/l2sw_driver.c line 200

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+		spin_unlock_irqrestore(&comm->ioctl_lock, flags);
+		pr_debug(" val_out = %04x\n", data->val_out);
+		break;
+
+	case SIOCSMIIREG:
+		spin_lock_irqsave(&comm->ioctl_lock, flags);
+		mdio_write(data->phy_id, data->reg_num, data->val_in);
+		spin_unlock_irqrestore(&comm->ioctl_lock, flags);
+		break;
+

You should be using phylink_mii_ioctl() or phy_mii_ioctl().

Quoted 1 line

You locking is also suspect.

Ok, I'll use phy_mii_ioctl() for SIOCSMIIREG and SIOCGMIIREG commands and remove spin_lock in next patch.

Quoted 10 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 214

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +7 -0
+static int ethernet_change_mtu(struct net_device *ndev, int new_mtu)
+{
+	if (netif_running(ndev))
+		return -EBUSY;
+
+	if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
+		return -EINVAL;

The core will do this for you, if you set the values in the ndev correct at probe time.

Ok, I'll remove the whole function ethernet_change_mtu () in next patch as the core will do everything for changing mtu.

Quoted 14 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 13 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +11 -0
+
+	ndev->mtu = new_mtu;
+
+	return 0;
+}
+
 
 
+static int mdio_access(u8 op_cd, u8 dev_reg_addr, u8 phy_addr, u32
+wdata) {
+	u32 value, time = 0;
+
+	HWREG_W(phy_cntl_reg0, (wdata << 16) | (op_cd << 13) |

(dev_reg_addr << 8) | phy_addr);

Quoted 3 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	wmb();			// make sure settings are effective.

That suggests you are using the wrong macros to access the registers.

Ok, I'll modify HWREG_W() and HWREG_R() and remove wmb().

Quoted 12 lines

drivers/net/ethernet/sunplus/l2sw_hal.c line 337

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +8 -0
+	do {
+		if (++time > MDIO_RW_TIMEOUT_RETRY_NUMBERS) {
+			pr_err(" mdio failed to operate!\n");
+			time = 0;
+		}
+
+		value = HWREG_R(phy_cntl_reg1);
+	} while ((value & 0x3) == 0);

include/linux/iopoll.h.

Quoted 5 lines

drivers/net/ethernet/sunplus/l2sw_hal.c line 340

Quoted 2 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +2 -0
+	if (time == 0)
+		return -1;

-ETIMDEOUT. One of the advantages of iopoll.h is that reusing code avoids issues like this.

Ok, I'll replace the do-while loop with read_poll_timeout().

Quoted 4 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+u32 mdio_read(u32 phy_id, u16 regnum) {
+	int ret;

Please check for C45 and return -EOPNOTSUPP.

Ok, I'll modify mdio_read() to return -EOPNOTSUPP
when error (time-out) occurs.

Quoted 5 lines
Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +5 -0
+
+	ret = mdio_access(MDIO_READ_CMD, regnum, phy_id, 0);
+	if (ret < 0)
+		return -EIO;
+
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_hal.c line 359

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +6 -0
+	return ret;
+}
+
+u32 mdio_write(u32 phy_id, u32 regnum, u16 val) {
+	int ret;
+

Please check for C45 and return -EOPNOTSUPP.

Ok, I'll modify mdio_read() to return -EOPNOTSUPP
when error (time-out) occurs.

Quoted 4 lines

drivers/net/ethernet/sunplus/l2sw_hal.c line 367

Quoted 2 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +2 -0
+
+inline void tx_trigger(void)

No inline functions in C code. Let the compiler decide.

Ok, I'll remove 'inline' for all functions in next patch.

Quoted 7 lines

drivers/net/ethernet/sunplus/l2sw_hal.c

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +6 -0
+int phy_cfg(struct l2sw_mac *mac)
+{
+	// Bug workaround:
+	// Flow-control of phy should be enabled. L2SW IP flow-control will refer
+	// to the bit to decide to enable or disable flow-control.
+	mdio_write(mac->comm->phy1_addr, 4,

mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));

Quoted 6 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	mdio_write(mac->comm->phy2_addr, 4,

mdio_read(mac->comm->phy2_addr,

Quoted 1 line

+4) | (1 << 10));

This should be in the PHY driver. The MAC driver should never need to touch PHY registers.

Sunplus Ethernet MAC integrates MDIO controller. So Ethernet driver has MDIO- and PHY-related code. To work-around a circuitry bug, we need to enable bit 10 of register 4 of PHY.
Where should we place the code?

Quoted 54 lines
Quoted 54 lines

+++ b/drivers/net/ethernet/sunplus/l2sw_mdio.c

drivers/net/ethernet/sunplus/l2sw_mdio.c new file +52 -0
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#include "l2sw_mdio.h"
+
+static int mii_read(struct mii_bus *bus, int phy_id, int regnum) {
+	return mdio_read(phy_id, regnum);
+}
+
+static int mii_write(struct mii_bus *bus, int phy_id, int regnum, u16
+val) {
+	return mdio_write(phy_id, regnum, val); }
+
+u32 mdio_init(struct platform_device *pdev, struct net_device
+*net_dev) {
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+	struct mii_bus *mii_bus;
+	struct device_node *mdio_node;
+	u32 ret;
+
+	mii_bus = mdiobus_alloc();
+	if (!mii_bus) {
+		pr_err(" 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->comm->phy1_node);
+	ret = of_mdiobus_register(mii_bus, mdio_node);
+	if (ret) {
+		pr_err(" Failed to register mii bus (ret = %d)!\n", ret);
+		mdiobus_free(mii_bus);
+		return ret;
+	}
+
+	mac->comm->mii_bus = mii_bus;
+	return ret;
+}
+
+void mdio_remove(struct net_device *net_dev) {
+	struct l2sw_mac *mac = netdev_priv(net_dev);
+
+	if (mac->comm->mii_bus) {
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_mdio.c line 59

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +5 -0
+		mdiobus_unregister(mac->comm->mii_bus);
+		mdiobus_free(mac->comm->mii_bus);
+		mac->comm->mii_bus = NULL;
+	}
+}

You MDIO code is pretty scattered around. Please bring it all together in one file.

Ok, I'll move mdio_read() and mdio_write() to 'l2sw_mdio.c', but keep mdio_access() in 'l2sw_hal.c' because it is a function which accesses hardware registers actually.

Quoted 4 lines

Review line 58

Quoted 1 line
(quoted diff) +1 -0
+static void mii_linkchange(struct net_device *netdev) { }

Nothing to do? Seems very odd. Don't you need to tell the MAC it should do 10Mbps or 100Mbps? What about pause?

No, hardware does it automatically.
Sunplus MAC integrates MDIO controller.
It reads PHY status and set MAC automatically.

Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_mdio.c

Quoted 7 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +7 -0
+
+int mac_phy_probe(struct net_device *netdev) {
+	struct l2sw_mac *mac = netdev_priv(netdev);
+	struct phy_device *phydev;
+	int i;
+
+	phydev = of_phy_connect(mac->net_dev, mac->comm->phy1_node,

mii_linkchange,

Quoted 4 lines

Review line 66

Quoted 1 line
(quoted diff) +1 -0
+				0, PHY_INTERFACE_MODE_RGMII_ID);

You should not hard code PHY_INTERFACE_MODE_RGMII_ID. Use the DT property "phy-mode"

Ok, I'll do it in next patch.
I create a new file 'l2sw_phy.c' and move phy-related functions from 'l2sw_hal.c' to it.

Quoted 7 lines

drivers/net/ethernet/sunplus/l2sw_mdio.c line 75

Quoted 3 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +3 -0
+	if (!phydev) {
+		pr_err(" \"%s\" has no phy found\n", netdev->name);
+		return -1;

-ENODEV;

Never use -1, pick an error code.

Ok, I'll modify it in next patch.

Quoted 11 lines

drivers/net/ethernet/sunplus/l2sw_mdio.c

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +4 -0
+	}
+
+	if (mac->comm->phy2_node) {
+		of_phy_connect(mac->net_dev, mac->comm->phy2_node,

mii_linkchange,

drivers/net/ethernet/sunplus/l2sw_mdio.c

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_mdio.c new file +4 -0
+			       0, PHY_INTERFACE_MODE_RGMII_ID);
+	}
+
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT,

phydev->supported);

Quoted 4 lines

Review line 79

Quoted 2 lines
(quoted diff) +2 -0
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+phydev->supported);

So the MAC does not support pause? I'm then confused about phy_cfg().

Yes, MAC supports pause. MAC (hardware) takes care of pause automatically.

Should I remove the two lines?

Quoted 2 lines

Andrew

#20 lore raw

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

Quoted 55 lines
Quoted 12 lines

drivers/net/ethernet/sunplus/Kconfig line 7

Quoted 2 lines
drivers/net/ethernet/sunplus/Kconfig new file +2 -0
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"

The "with L2 Switch" is causing lots of warning bells to ring for me.

I don't see any references to switchdev or DSA in this driver. How is the switch managed? There have been a few examples in the past of similar two port switches being first supported in Dual MAC mode. Later trying to actually use the switch in the Linux was always ran into problems, and basically needed a new driver. So i want to make sure you don't have this problem.

Quoted 8 lines

In the Linux world, Ethernet switches default to having there ports/interfaces separated. This effectively gives you your dual MAC mode by default. You then create a Linux bridge, and add the ports/interfaces to the bridge. switchdev is used to offload the bridge, telling the hardware to enable the L2 switch between the ports.

So you don't need the mode parameter in DT. switchdev tells you this. Switchdev gives user space access to the address table etc.

The L2 switch of Ethernet of SP7021 is not used to forward packets between two network interfaces.

Sunplus Dual Ethernet devices consists of one CPU port, two LAN ports, and a L2 switch. L2 switch is a circuitry which receives packets from CPU or LAN ports and then forwards them other ports. Rules of forwarding packets are set by driver.

Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
- Dual NIC mode
- An NIC with two LAN ports mode (daisy-chain mode)
- An NIC with two LAN ports mode 2

Dual NIC mode
Ethernet driver creates two net-device interfaces (eg: eth0 and eth1). Each has its dedicated LAN port. For example, LAN port 0 is for net-device interface eth0. LAN port 1 is for net-device interface eth1. Packets from LAN port 0 will be always forwarded to eth0 and vice versa by L2 switch. Similarly, packets from LAN port 1 will be always forwarded to eth1 and vice versa by L2 switch. Packets will never be forwarded between two LAN ports, or between eth0 and LAN port 1, or between eth1 and LAN port 0. The two network devices work independently.

An NIC with two LAN ports mode (daisy-chain mode) Ethernet driver creates one net-device interface (eg: eth0), but the net-device interface has two LAN ports. In this mode, a packet from one LAN port will be either forwarded to net-device interface (eht0) if its destination address matches MAC address of net-device interface (eth0), or forwarded to other LAN port. A packet from net-device interface (eth0) will be forwarded to a LAN port if its destination address is learnt by L2 switch, or forwarded to both LAN ports if its destination has not been learnt yet.

Quoted 7 lines

An NIC with two LAN ports mode 2
This mode is similar to “An NIC with two LAN ports mode”. The difference is that a packet from net-device interface (eth0) will be always forwarded to both LAN ports. Learning function of L2 switch is turned off in this mode. This means L2 switch will never learn the source address of a packet. So, it always forward packets to both LAN ports. This mode works like you have 2-port Ethernet hub.

So here you describe how the hardware can be used. Dual is two interfaces. Daisy-chain is what you get by taking those two interfaces and adding them to a bridge. The bridge then forwards frames between the interfaces and the CPU as needed, based on learning. And your third mode is the bridge always performs flooding.

A linux driver must follow the linux networking model. You cannot make up your own model. In the linux world, you model the external ports. The hardware always has two external ports, so you need to always have two netdev interfaces. To bridge packets between those two interfaces, you create a bridge and you add the interfaces to the bridge. That is the model you need to follow. switchdev gives you the API calls you need to implement this.

Quoted 10 lines
Quoted 4 lines
Quoted 1 line

+struct l2sw_common {

Please change your prefix. l2sw is a common prefix, there are other silicon vendors using l2sw. I would suggest sp_l2sw or spl2sw.

Ok, I'll modify two struct names in next patch as shown below:
l2sw_common --> sp_common
l2sw_mac --> sp_mac

Should I also modify prefix of file name?

You need to modify the prefix everywhere you use it. Function names, variable names, all symbols. Search and replace throughout the whole code.

Quoted 12 lines
Quoted 11 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+			return -EINVAL;
+		}
+	}
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1") ==
+0))

You cannot rely on the name, systemd has probably renamed it. If you have using phylib correctly, net_dev->phydev is what you want.

Quoted 2 lines

Ok, I'll use name of the second net device to do compare, instead of using fixed string "eth1", in next patch.

No. There are always two interfaces. You always have two netdev structures. Each netdev structure has a phydev. So use netdev->phydev.

This is another advantage of the Linux model. In your daisy chain mode, how do i control the two PHYs? How do i see one is up and one is down? How do i configure one to 10Half and the other 100Full?

Quoted 11 lines
Quoted 11 lines

drivers/net/ethernet/sunplus/l2sw_hal.c

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_hal.c new file +6 -0
+int phy_cfg(struct l2sw_mac *mac)
+{
+	// Bug workaround:
+	// Flow-control of phy should be enabled. L2SW IP flow-control will refer
+	// to the bit to decide to enable or disable flow-control.
+	mdio_write(mac->comm->phy1_addr, 4,

mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));

Review

Quoted 1 line
(quoted diff) +1 -0
+	mdio_write(mac->comm->phy2_addr, 4,

mdio_read(mac->comm->phy2_addr,

Quoted 1 line

+4) | (1 << 10));

Quoted 8 lines
Quoted 2 lines

This should be in the PHY driver. The MAC driver should never need to touch PHY registers.

Sunplus Ethernet MAC integrates MDIO controller. So Ethernet driver has MDIO- and PHY-related code. To work-around a circuitry bug, we need to enable bit 10 of register 4 of PHY.
Where should we place the code?

The silicon is integrated, but it is still a collection of standard blocks. Linux models those blocks independently. There is a subsystem for the MAC, a subsystem for the MDIO bus master and a subsystem for the PHY. You register a driver with each of these subsystems. PHY drivers live in drivers/net/phy. Put a PHY driver in there, which includes this workaround.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

+static void mii_linkchange(struct net_device *netdev) { }

Nothing to do? Seems very odd. Don't you need to tell the MAC it should do 10Mbps or 100Mbps? What about pause?

No, hardware does it automatically.
Sunplus MAC integrates MDIO controller.
It reads PHY status and set MAC automatically.

The PHY is external? So you have no idea what PHY that is? It could be a Marvell PHY, a microchip PHY, an Atheros PHY. Often PHYs have pages. In order to read the temperature sensor you change the page, read a register, and then hopefully change the page back again. If the PHY supports Fibre as well as copper, it can put the fibre registers in a second page. The PHY driver knows about this, it will flip the pages as needed. The phylib core has a mutex, so that only one operation happens at a time. So a page flip does not happen unexpectedly.

Your MAC hardware does not take this mutex. It has no idea what page is selected when it reads registers. Instead of getting the basic mode register, it could get the LED control register...

The MAC should never directly access the PHY. Please disable this hardware, and use the mii_linkchange callback to configure the MAC.

Quoted 6 lines
Quoted 1 line

So the MAC does not support pause? I'm then confused about phy_cfg().

Yes, MAC supports pause. MAC (hardware) takes care of pause automatically.

Should I remove the two lines?

Yes.

And you need to configure the MAC based on the results of the auto-neg.

Andrew

#21 lore raw

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

Quoted 69 lines
Quoted 55 lines
Quoted 12 lines

drivers/net/ethernet/sunplus/Kconfig line 7

Quoted 2 lines
drivers/net/ethernet/sunplus/Kconfig new file +2 -0
+config NET_VENDOR_SUNPLUS
+	tristate "Sunplus Dual 10M/100M Ethernet (with L2 switch) devices"

The "with L2 Switch" is causing lots of warning bells to ring for me.

I don't see any references to switchdev or DSA in this driver. How is the switch managed? There have been a few examples in the past of similar two port switches being first supported in Dual MAC mode. Later trying to actually use the switch in the Linux was always ran into problems, and basically needed a new driver. So i want to make sure you don't have this problem.

Quoted 8 lines

In the Linux world, Ethernet switches default to having there ports/interfaces separated. This effectively gives you your dual MAC mode by default. You then create a Linux bridge, and add the ports/interfaces to the bridge. switchdev is used to offload the bridge, telling the hardware to enable the L2 switch between the ports.

So you don't need the mode parameter in DT. switchdev tells you this. Switchdev gives user space access to the address table etc.

The L2 switch of Ethernet of SP7021 is not used to forward packets between two network interfaces.

Sunplus Dual Ethernet devices consists of one CPU port, two LAN ports, and a L2 switch. L2 switch is a circuitry which receives packets from CPU or LAN ports and then forwards them other ports. Rules of forwarding packets are set by driver.

Ethernet driver of SP7021 of Sunplus supports 3 operation modes:
- Dual NIC mode
- An NIC with two LAN ports mode (daisy-chain mode)
- An NIC with two LAN ports mode 2

Dual NIC mode
Ethernet driver creates two net-device interfaces (eg: eth0 and eth1). Each has its dedicated LAN port. For example, LAN port 0 is for net-device interface eth0. LAN port 1 is for net-device interface eth1. Packets from LAN port 0 will be always forwarded to eth0 and vice versa by L2 switch. Similarly, packets from LAN port 1 will be always forwarded to eth1 and vice versa by L2 switch. Packets will never be forwarded between two LAN ports, or between eth0 and LAN port 1, or between eth1 and LAN port 0. The two network devices work independently.

An NIC with two LAN ports mode (daisy-chain mode) Ethernet driver creates one net-device interface (eg: eth0), but the net-device interface has two LAN ports. In this mode, a packet from one LAN port will be either forwarded to net-device interface (eht0) if its destination address matches MAC address of net-device interface (eth0), or forwarded to other LAN port. A packet from net-device interface (eth0) will be forwarded to a LAN port if its destination address is learnt by L2 switch, or forwarded to both LAN ports if its destination has not been learnt yet.

Quoted 7 lines

An NIC with two LAN ports mode 2
This mode is similar to “An NIC with two LAN ports mode”. The difference is that a packet from net-device interface (eth0) will be always forwarded to both LAN ports. Learning function of L2 switch is turned off in this mode. This means L2 switch will never learn the source address of a packet. So, it always forward packets to both LAN ports. This mode works like you have 2-port Ethernet hub.

So here you describe how the hardware can be used. Dual is two interfaces. Daisy-chain is what you get by taking those two interfaces and adding them to a bridge. The bridge then forwards frames between the interfaces and the CPU as needed, based on learning. And your third mode is the bridge always performs flooding.

Quoted 6 lines

A linux driver must follow the linux networking model. You cannot make up your own model. In the linux world, you model the external ports. The hardware always has two external ports, so you need to always have two netdev interfaces. To bridge packets between those two interfaces, you create a bridge and you add the interfaces to the bridge. That is the model you need to follow. switchdev gives you the API calls you need to implement this.

Thank you very much for your explanation.

I realize that we need to follow the Linux networking model. I'll remove all descriptions about L2 switch or daisy-chain mode.

I'd like to modify Sunplus Ethernet driver to fulfill Linux networking model. Here is my proposal:

SP7021 Ethernet supports 3 operation modes:
- Dual Ethernet mode
In this mode, driver creates two net-device interfaces. Each connects to PHY. There are two LAN ports totally.
I am sorry that EMAC of SP7021 cannot support L2 switch functions of Linux switch-device model because it only has partial function of switch.

- One Ethernet mode
In this mode, driver creates one net-device interface. It connects to to a PHY (There is only one LAN port).
The LAN port is then connected to a 3-port Ethernet hub. The 3-port Ethernet hub is a hardware circuitry. All operations (packet forwarding) are done by hardware. No software intervention is needed. Actually, even just power-on, no software running, two LAN ports of SP7021 work well as 2-port hub.

- One Ethernet mode 2
This is mode is similar to previous mode, but a bit different settings to the hub.

Please kindly comment if my proposal is feasible or not

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line

+struct l2sw_common {

Please change your prefix. l2sw is a common prefix, there are other silicon vendors using l2sw. I would suggest sp_l2sw or spl2sw.

Quoted 8 lines
Quoted 5 lines

Ok, I'll modify two struct names in next patch as shown below:
l2sw_common --> sp_common
l2sw_mac --> sp_mac

Should I also modify prefix of file name?

You need to modify the prefix everywhere you use it. Function names, variable names, all symbols. Search and replace throughout the whole code.

Yes, I'll do in next patch.

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

drivers/net/ethernet/sunplus/l2sw_driver.c

Quoted 9 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +9 -0
+			return -EINVAL;
+		}
+	}
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		if (comm->dual_nic && (strcmp(ifr->ifr_ifrn.ifrn_name, "eth1")
+==
+0))

You cannot rely on the name, systemd has probably renamed it. If you have using phylib correctly, net_dev->phydev is what you want.

Quoted 5 lines
Quoted 2 lines

Ok, I'll use name of the second net device to do compare, instead of using fixed string "eth1", in next patch.

No. There are always two interfaces. You always have two netdev structures. Each netdev structure has a phydev. So use netdev->phydev.

Yes, I'll modify driver to use 'netdev->phydev'.

Quoted 3 lines

This is another advantage of the Linux model. In your daisy chain mode, how do i control the two PHYs? How do i see one is up and one is down? How do i configure one to 10Half and the other 100Full?

No software intervention is needed.
Hardware circuitry of EMAC of Sunplus SP7021 does it well. EMAC will communicate with PHY chips (via MDIO bus) automatically. Actually, just giving power to SP7021, the two LAN ports act as 2-port Ethernet hub, forwarding packets between ports.

Quoted 44 lines
Quoted 3 lines
Quoted 3 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+int phy_cfg(struct l2sw_mac *mac) {
+	// Bug workaround:
+	// Flow-control of phy should be enabled. L2SW IP flow-control will

refer

Quoted 7 lines
Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	// to the bit to decide to enable or disable flow-control.
+	mdio_write(mac->comm->phy1_addr, 4,

mdio_read(mac->comm->phy1_addr, 4) | (1 << 10));

Review

Quoted 1 line
(quoted diff) +1 -0
+	mdio_write(mac->comm->phy2_addr, 4,

mdio_read(mac->comm->phy2_addr,

Quoted 1 line

+4) | (1 << 10));

Quoted 8 lines
Quoted 2 lines

This should be in the PHY driver. The MAC driver should never need to touch PHY registers.

Sunplus Ethernet MAC integrates MDIO controller.
So Ethernet driver has MDIO- and PHY-related code.
To work-around a circuitry bug, we need to enable bit 10 of register 4 of PHY.
Where should we place the code?

The silicon is integrated, but it is still a collection of standard blocks. Linux models those blocks independently. There is a subsystem for the MAC, a subsystem for the MDIO bus master and a subsystem for the PHY. You register a driver with each of these subsystems. PHY drivers live in drivers/net/phy. Put a PHY driver in there, which includes this workaround.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

+static void mii_linkchange(struct net_device *netdev) { }

Nothing to do? Seems very odd. Don't you need to tell the MAC it should do 10Mbps or 100Mbps? What about pause?

No, hardware does it automatically.
Sunplus MAC integrates MDIO controller.
It reads PHY status and set MAC automatically.

The PHY is external? So you have no idea what PHY that is? It could be a Marvell PHY, a microchip PHY, an Atheros PHY. Often PHYs have pages. In order to read the temperature sensor you change the page, read a register, and then hopefully change the page back again. If the PHY supports Fibre as well as copper, it can put the fibre registers in a second page. The PHY driver knows about this, it will flip the pages as needed. The phylib core has a mutex, so that only one operation happens at a time. So a page flip does not happen unexpectedly.

Quoted 6 lines

Your MAC hardware does not take this mutex. It has no idea what page is selected when it reads registers. Instead of getting the basic mode register, it could get the LED control register...

The MAC should never directly access the PHY. Please disable this hardware, and use the mii_linkchange callback to configure the MAC.

Yes, the PHYs are external. SP7021 are connected to two ICPlus IP101 PHY. EMAC of SP7021 communicates with PHY via MDIO bus automatically after It is setup and enabled. Sorry that the function cannot be disabled.

The mentioned bug is not a bug of PHY, but a hardware bug of EMAC. SP7021 EMAC will read bit 10 (pause bit) of register 4 of PHY and then set pause mode of EMAC itself. At initial, bit 10 of register 4 of PHY is 0. This results in pause mode of EMAC be turned off. Due to timing issue, setting bit 10 on PHY driver is not feasible, we need to set it on EMAC driver right just after MAC is enabled.

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

So the MAC does not support pause? I'm then confused about phy_cfg().

Yes, MAC supports pause. MAC (hardware) takes care of pause automatically.

Should I remove the two lines?

Yes.

Yes, I'll remove them in next patch.

Quoted 3 lines

And you need to configure the MAC based on the results of the auto-neg.

Andrew

#22 lore raw

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

Quoted 7 lines

SP7021 Ethernet supports 3 operation modes:
- Dual Ethernet mode
In this mode, driver creates two net-device interfaces. Each connects to PHY. There are two LAN ports totally.
I am sorry that EMAC of SP7021 cannot support L2 switch functions of Linux switch-device model because it only has partial function of switch.

This is fine.

Quoted 9 lines

- One Ethernet mode
In this mode, driver creates one net-device interface. It connects to to a PHY (There is only one LAN port).
The LAN port is then connected to a 3-port Ethernet hub. The 3-port Ethernet hub is a hardware circuitry. All operations (packet forwarding) are done by hardware. No software intervention is needed. Actually, even just power-on, no software running, two LAN ports of SP7021 work well as 2-port hub.

We need to dig into the details of this mode. I would initially say no, until we really do know it is impossible to do it correctly. Even if it is impossible to do it correctly, i'm still temped to reject this mode.

How does spanning tree work? Who sends and receives the BPDU?

Is there PTP support? How do you send and receive the PTP frames?

Is IGMP snooping supported?

All of these have one thing in common, you need to be able to egress frames out a specific port of the switch, and you need to know what port a received frames ingressed on. If you can do that, you can probably do proper support in Linux.

Is the datasheet available?

Andrew

#23 lore raw

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

Quoted 9 lines
Quoted 7 lines

SP7021 Ethernet supports 3 operation modes:
- Dual Ethernet mode
In this mode, driver creates two net-device interfaces. Each connects to PHY. There are two LAN ports totally.
I am sorry that EMAC of SP7021 cannot support L2 switch functions of Linux switch-device model because it only has partial function of switch.

This is fine.

Thanks a lot!

Quoted 16 lines
Quoted 9 lines

- One Ethernet mode
In this mode, driver creates one net-device interface. It connects to to a PHY (There is only one LAN port).
The LAN port is then connected to a 3-port Ethernet hub. The 3-port Ethernet hub is a hardware circuitry. All operations (packet forwarding) are done by hardware. No software intervention is needed. Actually, even just power-on, no software running, two LAN ports of SP7021 work well as 2-port hub.

We need to dig into the details of this mode. I would initially say no, until we really do know it is impossible to do it correctly. Even if it is impossible to do it correctly, i'm still temped to reject this mode.

How does spanning tree work? Who sends and receives the BPDU?

Quoted 8 lines

Is there PTP support? How do you send and receive the PTP frames?

Is IGMP snooping supported?

All of these have one thing in common, you need to be able to egress frames out a specific port of the switch, and you need to know what port a received frames ingressed on. If you can do that, you can probably do proper support in Linux.

The "L2 switch" is a very simple witch. It has 3 ports: CPU, LAN port 0 and LAN port 1. A packet is always forwarded to other two ports if source-address (of MAC) learning function is off, or forwarded to one of the two ports if source-address learning function is on and source address is learnt (recorded by switch).

The switch will not recognize type of packets, regardless BPDU, PTP or any other packets. If turning off source-address learning function, it works like an Ethernet plus a 2-port hub.

Quoted 1 line

Is the datasheet available?

Yes, refer to on-line document of SP7021:
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/pages/462553090/15.+Ethernet+Switch

Quoted 2 lines

Andrew

#24 lore raw

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

Quoted 3 lines

The switch will not recognize type of packets, regardless BPDU, PTP or any other packets. If turning off source-address learning function, it works like an Ethernet plus a 2-port hub.

So without STP, there is no way to stop an loop, and a broadcast storm taking down your network?

Looking at the TX descriptor, there are two bits:

[18]: force forward to port 0
[19]: force forward to port 1

When the switch is enabled, can these two bits be used?

In the RX descriptor there is:

pkt_sp:

          000: from port0
          001: from port1
          110: soc0 loopback
          101: soc1 loopback

Are these bits used when the switch is enabled?

0.31 port control 1 (port cntl1) blocking state seems to have what you need for STP.

Andrew

#25 lore raw

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

Quoted 6 lines
Quoted 3 lines

The switch will not recognize type of packets, regardless BPDU, PTP or any other packets. If turning off source-address learning function, it works like an Ethernet plus a 2-port hub.

So without STP, there is no way to stop an loop, and a broadcast storm taking down your network?

Do you mean connecting two PHY ports to the same LAN? We never connect two PHY ports to the same LAN (or hub). I never think of this loop problem. I thought only WAN has the loop problem.

The switch has some kinds of flow control, refer to 0.2 "Flow control threshold" and 0.3 "CPU port flow control threshold". It will drop extra packets. How an Ethernet hub take care of this situation?
Is that reasonable to connect two ports of an Ethernet hub together?

Quoted 6 lines

Looking at the TX descriptor, there are two bits:

[18]: force forward to port 0
[19]: force forward to port 1

When the switch is enabled, can these two bits be used?

Yes, for example, when bit 19 of TX descriptor is enabled, a packet from CPU port is forwarded to LAN port 0 forcibly.

Quoted 9 lines

In the RX descriptor there is:

pkt_sp:

          000: from port0
          001: from port1
          110: soc0 loopback
          101: soc1 loopback

Are these bits used when the switch is enabled?

Yes, E- MAC driver uses these bits to tell where a packet comes from. Note that soc1 port (CPU port) has been removed in this chip.

Quoted 2 lines

0.31 port control 1 (port cntl1) blocking state seems to have what you need for STP.

From document, if bit 17 or bit 16 of port_cntl1 register is set, only RMC packets will be forwarded to other LAN port. I am not sure whether enabling the bits helps the issue. Should I enable the bits? Sorry, I don't know what is a RMC packet?
Could you please teach me?

Quoted 1 line

Andrew

Thank you for review.

#26 lore raw

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

Quoted 10 lines · Wells Lu 呂芳騰
Quoted 6 lines
Quoted 3 lines

The switch will not recognize type of packets, regardless BPDU, PTP or any other packets. If turning off source-address learning function, it works like an Ethernet plus a 2-port hub.

So without STP, there is no way to stop an loop, and a broadcast storm taking down your network?

Do you mean connecting two PHY ports to the same LAN? We never connect two PHY ports to the same LAN (or hub). I never think of this loop problem. I thought only WAN has the loop problem.

Any Ethernet network can have a loop. Often loops a deliberate because they give redundancy. STP will detect this loop, and somewhere in the network one of the switches will block traffic to break the loop. But if something in the network breaks, the port can be unblocked to allow traffic to flow, redundancy. Well behaved switches should always implement STP.

Quoted 1 line

How an Ethernet hub take care of this situation?

STP. Run tcpdump on your network. Depending on how your network is configured, you might see BPDU from your building switches.

Quoted 1 line

Is that reasonable to connect two ports of an Ethernet hub together?

It is not just together. You cannot guarantee any Ethernet network is a tree. You could connect the two ports to two different hubs, but those hubs are connected together, and so you get a loop.

Quoted 19 lines
Quoted 6 lines

Looking at the TX descriptor, there are two bits:

[18]: force forward to port 0
[19]: force forward to port 1

When the switch is enabled, can these two bits be used?

Yes, for example, when bit 19 of TX descriptor is enabled, a packet from CPU port is forwarded to LAN port 0 forcibly.

Quoted 8 lines

In the RX descriptor there is:

pkt_sp:

          000: from port0
          001: from port1
          110: soc0 loopback
          101: soc1 loopback
Quoted 4 lines
Quoted 1 line

Are these bits used when the switch is enabled?

Yes, E- MAC driver uses these bits to tell where a packet comes from. Note that soc1 port (CPU port) has been removed in this chip.

Right. So you can have two netdev when in L2 switch mode.

You need to think about the Linux model some more. In linux, networking hardware is there to accelerate what the Linux stack can do in software. Take for example a simple SoC will have two Ethernet interfaces. You can perform software bridging on those two interfaces:

ip link add name br0 type bridge
ip link set dev br0 up
ip link set dev eth0 master br0
ip link set dev eth1 master br0

The software bridge will decided which interface to send a packet out. The software will perform learning etc.

You can use your dual MAC setup exactly like this. But you can also go further. You can use the hardware to accelerate switching packets between eth0 and eth1. But also Linux can still send packets out specific ports using these bits. The software bridge and the hardware bridge work together. This is the correct way to do this in Linux.

Quoted 1 line

Sorry, I don't know what is a RMC packet?

Sorry, i have no idea.

Andrew

#27 lore raw

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

Quoted 12 lines · Wells Lu 呂芳騰
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines

The switch will not recognize type of packets, regardless BPDU, PTP or any other packets. If turning off source-address learning function, it works like an Ethernet plus a 2-port hub.

So without STP, there is no way to stop an loop, and a broadcast storm taking down your network?

Do you mean connecting two PHY ports to the same LAN? We never connect two PHY ports to the same LAN (or hub). I never think of this loop problem. I thought only WAN has the loop problem.

Quoted 5 lines

Any Ethernet network can have a loop. Often loops a deliberate because they give redundancy. STP will detect this loop, and somewhere in the network one of the switches will block traffic to break the loop. But if something in the network breaks, the port can be unblocked to allow traffic to flow, redundancy. Well behaved switches should always implement STP.

I don't know how to implement STP in L2 switch like SP7021.
How about one NIC + 2-port simple frame-flooding hub?
Someone told me that some low-cost Ethernet hub just does frame-flooding to other ports. Let users take care of use.
If this is acceptable, I'd like to have Ethernet of SP7021 have two operation modes:
- Dual NIC mode
- Single NIC with 2-port frame-flooding hub mode

If this is not acceptable, can I, instead, implement the two operation modes:
- Dual NIC mode
- Single NIC mode

RMII pins of PHY ports of SP7021 are multiplexable. I'd like to switch RMII pins of the second PHY for other use if single NIC mode is used. In fact, some SP7021 boards have dual Ethernet and some have only one Ethernet. We really need the two operation modes.

Quoted 4 lines
Quoted 1 line

How an Ethernet hub take care of this situation?

STP. Run tcpdump on your network. Depending on how your network is configured, you might see BPDU from your building switches.

Thanks a lot. I understand.

Quoted 5 lines
Quoted 1 line

Is that reasonable to connect two ports of an Ethernet hub together?

It is not just together. You cannot guarantee any Ethernet network is a tree. You could connect the two ports to two different hubs, but those hubs are connected together, and so you get a loop.

Thanks for explanation. I got it.

Quoted 36 lines
Quoted 19 lines
Quoted 6 lines

Looking at the TX descriptor, there are two bits:

[18]: force forward to port 0
[19]: force forward to port 1

When the switch is enabled, can these two bits be used?

Yes, for example, when bit 19 of TX descriptor is enabled, a packet from CPU port is forwarded to LAN port 0 forcibly.

Quoted 8 lines

In the RX descriptor there is:

pkt_sp:

          000: from port0
          001: from port1
          110: soc0 loopback
          101: soc1 loopback
Quoted 4 lines
Quoted 1 line

Are these bits used when the switch is enabled?

Yes, E- MAC driver uses these bits to tell where a packet comes from. Note that soc1 port (CPU port) has been removed in this chip.

Right. So you can have two netdev when in L2 switch mode.

You need to think about the Linux model some more. In linux, networking hardware is there to accelerate what the Linux stack can do in software. Take for example a simple SoC will have two Ethernet interfaces. You can perform software bridging on those two interfaces:

ip link add name br0 type bridge
ip link set dev br0 up
ip link set dev eth0 master br0
ip link set dev eth1 master br0

Quoted 8 lines

The software bridge will decided which interface to send a packet out. The software will perform learning etc.

You can use your dual MAC setup exactly like this. But you can also go further. You can use the hardware to accelerate switching packets between eth0 and eth1. But also Linux can still send packets out specific ports using these bits. The software bridge and the hardware bridge work together. This is the correct way to do this in Linux.

Sorry, I am not capable to do that.
I need to study more about Linux switch device, L2 switch of SP7021, procotols,... So what I can use now is pure software bridge for dual Ethernet case.

Quoted 3 lines
Quoted 1 line

Sorry, I don't know what is a RMC packet?

Sorry, i have no idea.

After looking up some data, I find RMC means reserved multi-cast.
RMC packets means packets with DA = 0x0180c2000000, 0x0180c2000002 ~ 0x0180c200000f, except the PAUSE packet (DA = 0x0180c2000001)

Quoted 1 line

Andrew

Thank you for your review.

#28 lore raw

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

Quoted 1 line

I don't know how to implement STP in L2 switch like SP7021.

That is the nice thing about using Linux. It already knows how to implement STP. The bridge will do it for you. You just need to add the callbacks in the driver which are needed. Please take a look at other switchdev drivers.

Quoted 4 lines

If this is acceptable, I'd like to have Ethernet of SP7021 have two operation modes:
- Dual NIC mode
- Single NIC with 2-port frame-flooding hub mode

No, sorry. Do it correctly, or do not do it. Please start with a clean driver doing Dual NIC mode. You can add L2 support later, once you have done the research to understand switchdev, etc.

Quoted 4 lines

RMII pins of PHY ports of SP7021 are multiplexable. I'd like to switch RMII pins of the second PHY for other use if single NIC mode is used. In fact, some SP7021 boards have dual Ethernet and some have only one Ethernet. We really need the two operation modes.

Only using a subset of ports in a switch is common. The common binding for DSA switches is described in:

Documentation/devicetree/bindings/net/dsa/dsa.yaml and for example Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml is a memory mapped switch. Notice the reg numbers:

           ethernet-ports {
                #address-cells = <1>;
                #size-cells = <0>;

                port@0 {
                    reg = <0>;
                    label = "cpu";
                    ethernet = <&gmac0>;
                };

                port@2 {
                    reg = <2>;
                    label = "lan0";
                    phy-handle = <&phy1>;
                };

reg = <1> is missing in this example. Port 1 of the switch is not used. You can do the same with a 2 port switch, when you don't want to make use of a port. Just don't list it in DT.

Quoted 3 lines

After looking up some data, I find RMC means reserved multi-cast.
RMC packets means packets with DA = 0x0180c2000000, 0x0180c2000002 ~ 0x0180c200000f, except the PAUSE packet (DA = 0x0180c2000001)

Ah, good. BPDUs use 01:80:C2:00:00:00. So they will be passed when the port is in blocking mode. PTP uses 01:80:C2:00:00:0E. So the hardware designers appear to of designed a proper L2 switch with everything you need for a managed switch. What is missing is software. The more i learn about this hardware, the more i've convinced you need to write proper Linux support for it, not your mode hacks.

Andrew

#29 lore raw

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

Quoted 12 lines
Quoted 1 line

I don't know how to implement STP in L2 switch like SP7021.

That is the nice thing about using Linux. It already knows how to implement STP. The bridge will do it for you. You just need to add the callbacks in the driver which are needed. Please take a look at other switchdev drivers.

Quoted 5 lines

If this is acceptable, I'd like to have Ethernet of SP7021 have two operation
modes:
- Dual NIC mode
- Single NIC with 2-port frame-flooding hub mode

Quoted 3 lines

No, sorry. Do it correctly, or do not do it. Please start with a clean driver doing Dual NIC mode. You can add L2 support later, once you have done the research to understand switchdev, etc.

Sorry, I will go with Dual NIC mode. I'll do a whole cleanup on driver for this. Please kindly review again.

I need time to study more about switchdev and propose a plan to high management of company. However, Sunplus is not a networking company, but targets on Linux-based industrial control, autonomous mobile robot, ...

Quoted 28 lines
Quoted 4 lines

RMII pins of PHY ports of SP7021 are multiplexable. I'd like to switch RMII pins of the second PHY for other use if single NIC mode is used. In fact, some SP7021 boards have dual Ethernet and some have only one Ethernet. We really need the two operation modes.

Only using a subset of ports in a switch is common. The common binding for DSA switches is described in:

Documentation/devicetree/bindings/net/dsa/dsa.yaml and for example Documentation/devicetree/bindings/net/dsa/hirschmann,hellcreek.yaml is a memory mapped switch. Notice the reg numbers:

           ethernet-ports {
                #address-cells = <1>;
                #size-cells = <0>;

                port@0 {
                    reg = <0>;
                    label = "cpu";
                    ethernet = <&gmac0>;
                };

                port@2 {
                    reg = <2>;
                    label = "lan0";
                    phy-handle = <&phy1>;
                };
Quoted 3 lines

reg = <1> is missing in this example. Port 1 of the switch is not used. You can do the same with a 2 port switch, when you don't want to make use of a port. Just don't list it in DT.

Thank you for routing me to the document.
Now I know there are switch device examples in folder dsa/. We can refer to them when we want to make a switch.

Quoted 4 lines
Quoted 3 lines

After looking up some data, I find RMC means reserved multi-cast. RMC packets means packets with DA = 0x0180c2000000, 0x0180c2000002 ~ 0x0180c200000f, except the PAUSE packet (DA = 0x0180c2000001)

Quoted 8 lines

Ah, good. BPDUs use 01:80:C2:00:00:00. So they will be passed when the port is in blocking mode. PTP uses 01:80:C2:00:00:0E. So the hardware designers appear to of designed a proper L2 switch with everything you need for a managed switch. What is missing is software. The more i learn about this hardware, the more i've convinced you need to write proper Linux support for it, not your mode hacks.

Andrew

Thanks for confirming that the L2 switch is good enough for switch device. Actually, the IP was licensed from other company long ago. We don’t know all details.

#30 lore raw

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

In reply to Wells Lu

Hi, Wells!

On 11/3/21 14:02, Wells Lu wrote:

[code snip]

drivers/net/ethernet/sunplus/l2sw_driver.c line 426

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+		if (comm->dual_nic) {
+			struct net_device *net_dev2 = mac->next_netdev;
+
+			if (!netif_running(net_dev2)) {
+				mac_hw_stop(mac);
+
+				mac2 = netdev_priv(net_dev2);
+

(*)

drivers/net/ethernet/sunplus/l2sw_driver.c line 437

Quoted 11 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +11 -0
+				// unregister and free net device.
+				unregister_netdev(net_dev2);
+				free_netdev(net_dev2);
+				mac->next_netdev = NULL;
+				pr_info(" Unregistered and freed net device \"eth1\"!\n");
+
+				comm->dual_nic = 0;
+				mac_switch_mode(mac);
+				rx_mode_set(net_dev);
+				mac_hw_addr_del(mac2);
+

mac2 is net_dev2 private data (*), so it will become freed after free_netdev() call.

FWIW the latest smatch should warn about this type of bugs.

drivers/net/ethernet/sunplus/l2sw_driver.c line 441

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +4 -0
+				// If eth0 is up, turn on lan 0 and 1 when
+				// switching to daisy-chain mode.
+				if (comm->enable & 0x1)
+					comm->enable = 0x3;

[code snip]

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+static int l2sw_remove(struct platform_device *pdev)
+{
+	struct net_device *net_dev;
+	struct net_device *net_dev2;
+	struct l2sw_mac *mac;
+
+	net_dev = platform_get_drvdata(pdev);
+	if (!net_dev)

drivers/net/ethernet/sunplus/l2sw_driver.c line 734

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+		return 0;
+	mac = netdev_priv(net_dev);
+
+	// Unregister and free 2nd net device.
+	net_dev2 = mac->next_netdev;
+	if (net_dev2) {
+		unregister_netdev(net_dev2);
+		free_netdev(net_dev2);
+	}
+

Is it save here to free mac->next_netdev before unregistering "parent" netdev? I haven't checked the whole code, just asking :)

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+	sysfs_remove_group(&pdev->dev.kobj, &l2sw_attribute_group);
+
+	mac->comm->enable = 0;
+	soc0_stop(mac);
+
+	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);
+

drivers/net/ethernet/sunplus/l2sw_driver.c line 754

Quoted 10 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +10 -0
+	mdio_remove(net_dev);
+
+	// Unregister and free 1st net device.
+	unregister_netdev(net_dev);
+	free_netdev(net_dev);
+
+	clk_disable(mac->comm->clk);
+
+	// Free 'common' area.
+	kfree(mac->comm);

Same here with mac.

Review

Quoted 2 lines
(quoted diff) +2 -0
+	return 0;
+}

I haven't read the whole thread, i am sorry if these questions were already discussed.

With regards,
Pavel Skripkin

#31 lore raw

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

Hi Pavel,

Quoted 32 lines

Hi, Wells!

On 11/3/21 14:02, Wells Lu wrote:

[code snip]

drivers/net/ethernet/sunplus/l2sw_driver.c line 426

Quoted 8 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +8 -0
+		if (comm->dual_nic) {
+			struct net_device *net_dev2 = mac->next_netdev;
+
+			if (!netif_running(net_dev2)) {
+				mac_hw_stop(mac);
+
+				mac2 = netdev_priv(net_dev2);
+

(*)

drivers/net/ethernet/sunplus/l2sw_driver.c line 437

Quoted 11 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +11 -0
+				// unregister and free net device.
+				unregister_netdev(net_dev2);
+				free_netdev(net_dev2);
+				mac->next_netdev = NULL;
+				pr_info(" Unregistered and freed net device \"eth1\"!\n");
+
+				comm->dual_nic = 0;
+				mac_switch_mode(mac);
+				rx_mode_set(net_dev);
+				mac_hw_addr_del(mac2);
+

mac2 is net_dev2 private data (*), so it will become freed after free_netdev() call.

Quoted 1 line

FWIW the latest smatch should warn about this type of bugs.

Yes, this is indeed a bug.
But the code paragraph has been removed thoroughly in [PATCH v2].

Quoted 19 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 441

Quoted 4 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +4 -0
+				// If eth0 is up, turn on lan 0 and 1 when
+				// switching to daisy-chain mode.
+				if (comm->enable & 0x1)
+					comm->enable = 0x3;

[code snip]

Quoted 12 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +12 -0
+static int l2sw_remove(struct platform_device *pdev) {
+	struct net_device *net_dev;
+	struct net_device *net_dev2;
+	struct l2sw_mac *mac;
+
+	net_dev = platform_get_drvdata(pdev);
+	if (!net_dev)
+		return 0;
+	mac = netdev_priv(net_dev);
+
+	// Unregister and free 2nd net device.
+	net_dev2 = mac->next_netdev;
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 454

Quoted 5 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +5 -0
+	if (net_dev2) {
+		unregister_netdev(net_dev2);
+		free_netdev(net_dev2);
+	}
+

Is it save here to free mac->next_netdev before unregistering "parent" netdev? I haven't checked the whole code, just asking :)

Yes, I think it is save.
netdev2 should be unregistered and freed before net_dev. If net_dev is unregistered and freed in advance, mac->next_netdev becomes danger because 'mac' has been freed.

Quoted 14 lines
Quoted 14 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +14 -0
+	sysfs_remove_group(&pdev->dev.kobj, &l2sw_attribute_group);
+
+	mac->comm->enable = 0;
+	soc0_stop(mac);
+
+	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);
+
+	mdio_remove(net_dev);
+
+	// Unregister and free 1st net device.
+	unregister_netdev(net_dev);
Quoted 8 lines

drivers/net/ethernet/sunplus/l2sw_driver.c line 754

Quoted 6 lines
drivers/net/ethernet/sunplus/l2sw_driver.c new file +6 -0
+	free_netdev(net_dev);
+
+	clk_disable(mac->comm->clk);
+
+	// Free 'common' area.
+	kfree(mac->comm);

Same here with mac.

This is indeed a bug.
But the statement, Kfree(mac->comm);, has been removed in [PATCH v2]. In [PATCH v2], structure data 'mac->comm' is allocated by devm_kzalloc(). No more need to free it here.

Quoted 10 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+	return 0;
+}

I haven't read the whole thread, i am sorry if these questions were already discussed.

With regards,
Pavel Skripkin

Thank you very much for your review!

Best regards,
Wells Lu

#32 lore raw
In reply to Wells Lu

11/3/21 2:02 PM, Wells Lu пишет:

Quoted 4 lines

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.

The series should pe prefixes with net-next

Quoted 48 lines

Refer to:
https://sunplus-tibbo.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-l2sw.yaml          | 123 ++++
  MAINTAINERS                                        |   8 +
  drivers/net/ethernet/Kconfig                       |   1 +
  drivers/net/ethernet/Makefile                      |   1 +
  drivers/net/ethernet/sunplus/Kconfig               |  20 +
  drivers/net/ethernet/sunplus/Makefile              |   6 +
  drivers/net/ethernet/sunplus/l2sw_define.h         | 221 ++++++
  drivers/net/ethernet/sunplus/l2sw_desc.c           | 233 ++++++
  drivers/net/ethernet/sunplus/l2sw_desc.h           |  21 +
  drivers/net/ethernet/sunplus/l2sw_driver.c         | 779 +++++++++++++++++++++
  drivers/net/ethernet/sunplus/l2sw_driver.h         |  23 +
  drivers/net/ethernet/sunplus/l2sw_hal.c            | 422 +++++++++++
  drivers/net/ethernet/sunplus/l2sw_hal.h            |  47 ++
  drivers/net/ethernet/sunplus/l2sw_int.c            | 326 +++++++++
  drivers/net/ethernet/sunplus/l2sw_int.h            |  16 +
  drivers/net/ethernet/sunplus/l2sw_mac.c            |  68 ++
  drivers/net/ethernet/sunplus/l2sw_mac.h            |  24 +
  drivers/net/ethernet/sunplus/l2sw_mdio.c           | 118 ++++
  drivers/net/ethernet/sunplus/l2sw_mdio.h           |  19 +
  drivers/net/ethernet/sunplus/l2sw_register.h       |  99 +++
  20 files changed, 2575 insertions(+)

create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-l2sw.yaml create mode 100644 drivers/net/ethernet/sunplus/Kconfig
create mode 100644 drivers/net/ethernet/sunplus/Makefile
create mode 100644 drivers/net/ethernet/sunplus/l2sw_define.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_desc.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_driver.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_hal.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_int.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_mac.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.c
create mode 100644 drivers/net/ethernet/sunplus/l2sw_mdio.h
create mode 100644 drivers/net/ethernet/sunplus/l2sw_register.h

author
#33 lore raw

v2 cover This is a patch series for pinctrl driver for Sunplus SP7021 SoC.

In reply to 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-tibbo.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          | 152 ++++++
 MAINTAINERS                                        |   8 +
 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 ++++
 22 files changed, 2330 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/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
Signature · 1 line
2.7.4
author
#34 lore raw

PATCH v2 1/2 devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

Add bindings documentation for Sunplus SP7021.

---
Changes in V2
- Added mdio and phy sub-nodes.

 .../bindings/net/sunplus,sp7021-emac.yaml          | 152 +++++++++++++++++++++
 MAINTAINERS                                        |   7 +
 2 files changed, 159 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +152 -0
new file mode 100644
index 0000000..9bb0b4a
@@ -0,0 +1,152 @@
+# 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 <[email protected]>
+
+description: |
+  Sunplus SP7021 dual 10M/100M Ethernet MAC controller.
+  Device node of the controller has following properties.
+
+properties:
+  compatible:
+    const: sunplus,sp7021-emac
+
+  reg:
+    items:
+      - description: Base address and length of the EMAC registers.
+      - description: Base address and length of the MOON5 registers.
+
+  reg-names:
+    items:
+      - const: emac
+      - const: moon5
+
+  interrupts:
+    description: |
+      Contains number and type of interrupt. Number should be 66.
+      Type should be high-level trigger
+    maxItems: 1
+
+  clocks:
+    description: |
+      Clock controller selector for Ethernet MAC controller.
+    maxItems: 1
+
+  resets:
+    description: |
+      Reset controller selector for Ethernet MAC controller.
+    maxItems: 1
+
+  phy-handle1:
+    description: A handle to node of phy 1 in mdio node
+    maxItems: 1
+
+  phy-handle2:
+    description: A handle to node of phy 2 in mdio node
+    maxItems: 1
+
+  pinctrl-names:
+    description: |
+      Names corresponding to the numbered pinctrl states.
+      A pinctrl state named "default" must be defined.
+    const: default
+
+  pinctrl-0:
+    description: A handle to the 'default' state of pin configuration
+
+  nvmem-cells:
+    items:
+      - description: nvmem cell address of MAC address of MAC 1
+      - description: nvmem cell address of MAC address of MAC 2
+
+  nvmem-cell-names:
+    description: names corresponding to the nvmem cells of MAC address
+    items:
+      - const: mac_addr0
+      - const: mac_addr1
+
+  mdio:
+    type: object
+    description: Internal MDIO Bus
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+    patternProperties:
+      "^ethernet-phy@[0-9]$":
+        type: object
+
+        description:
+          Integrated PHY node
+
+        properties:
+           reg:
+             maxItems: 1
+
+           phy-mode:
+             maxItems: 1
+
+        required:
+          - reg
+          - phy-mode
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - clocks
+  - resets
+  - phy-handle1
+  - phy-handle2
+  - pinctrl-0
+  - pinctrl-names
+  - nvmem-cells
+  - nvmem-cell-names
+  - mdio
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    emac: emac@9c108000 {
+        compatible = "sunplus,sp7021-emac";
+        reg = <0x9c108000 0x400>, <0x9c000280 0x80>;
+        reg-names = "emac", "moon5";
+        interrupt-parent = <&intc>;
+        interrupts = <66 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clkc 0xa7>;
+        resets = <&rstc 0x97>;
+        phy-handle1 = <&eth_phy0>;
+        phy-handle2 = <&eth_phy1>;
+        pinctrl-0 = <&emac_demo_board_v3_pins>;
+        pinctrl-names = "default";
+        nvmem-cells = <&mac_addr0>, <&mac_addr1>;
+        nvmem-cell-names = "mac_addr0", "mac_addr1";
+
+        mdio {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            eth_phy0: ethernet-phy@0 {
+                reg = <0>;
+                phy-mode = "rmii";
+            };
+            eth_phy1: ethernet-phy@1 {
+                reg = <1>;
+                phy-mode = "rmii";
+            };
+        };
+    };
+...
MAINTAINERS +7 -0
index dcc1819..737b9d0 100644
@@ -18000,6 +18000,13 @@ L:	[email protected]
 S:	Maintained
 F:	drivers/net/ethernet/dlink/sundance.c
 
+SUNPLUS ETHERNET DRIVER
+M:	Wells Lu <[email protected]>
+L:	[email protected]
+S:	Maintained
+W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml
+
 SUPERH
 M:	Yoshinori Sato <[email protected]>
 M:	Rich Felker <[email protected]>
Signature · 1 line
2.7.4
maintainer
#35 lore raw

Re: [PATCH v2 1/2] devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

Quoted 12 lines · Wells Lu

Add bindings documentation for Sunplus SP7021.

---
Changes in V2
- Added mdio and phy sub-nodes.

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

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:
./Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml:94:12: [warning] wrong indentation: expected 10 but found 11 (indentation)

dtschema/dtc warnings/errors:

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553831

This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.

If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:

pip3 install dtschema --upgrade

Please check and re-submit.

#36 lore raw

RE: [PATCH v2 1/2] devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

Hi,

Quoted 31 lines · Wells Lu
Quoted 7 lines

Add bindings documentation for Sunplus SP7021.

---
Changes in V2
- Added mdio and phy sub-nodes.

Quoted 6 lines
 .../bindings/net/sunplus,sp7021-emac.yaml          | 152 +++++++++++++++++++++
 MAINTAINERS                                        |   7 +
 2 files changed, 159 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:
./Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml:94:12: [warning] wrong
indentation: expected 10 but found 11 (indentation)

dtschema/dtc warnings/errors:

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/patch/1553831

This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.

Quoted 7 lines

If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit.

Thank you very much for review and tests.
I'll fix wrong indentation next patch.

#37 lore raw

Re: [PATCH v2 1/2] devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

In reply to Wells Lu
Quoted 14 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +14 -0
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    emac: emac@9c108000 {
+        compatible = "sunplus,sp7021-emac";
+        reg = <0x9c108000 0x400>, <0x9c000280 0x80>;
+        reg-names = "emac", "moon5";
+        interrupt-parent = <&intc>;
+        interrupts = <66 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clkc 0xa7>;
+        resets = <&rstc 0x97>;
+        phy-handle1 = <&eth_phy0>;
+        phy-handle2 = <&eth_phy1>;
+        pinctrl-0 = <&emac_demo_board_v3_pins>;

Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml line 144

Quoted 10 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +10 -0
+        pinctrl-names = "default";
+        nvmem-cells = <&mac_addr0>, <&mac_addr1>;
+        nvmem-cell-names = "mac_addr0", "mac_addr1";
+
+        mdio {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            eth_phy0: ethernet-phy@0 {
+                reg = <0>;
+                phy-mode = "rmii";

This is in the wrong place. It is a MAC property. You usually put it next to phy-handle.

Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml line 150

Quoted 6 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +6 -0
+            };
+            eth_phy1: ethernet-phy@1 {
+                reg = <1>;
+                phy-mode = "rmii";
+            };
+        };

I would suggest you structure this differently to make it clear it is a two port switch:

	ethernet-ports {
		#address-cells = <1>;
                #size-cells = <0>;

                port@0 {
                    reg = <0>;
		    phy-handle = <&eth_phy0>;
		    phy-mode = "rmii";
		}

		port@1 {
                    reg = <1>;
		    phy-handle = <&eth_phy1>;
		    phy-mode = "rmii";
		}
	}

	Andrew
#38 lore raw

RE: [PATCH v2 1/2] devicetree: bindings: net: Add bindings doc for Sunplus SP7021.

Hi,

Quoted 18 lines
Quoted 18 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +18 -0
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    emac: emac@9c108000 {
+        compatible = "sunplus,sp7021-emac";
+        reg = <0x9c108000 0x400>, <0x9c000280 0x80>;
+        reg-names = "emac", "moon5";
+        interrupt-parent = <&intc>;
+        interrupts = <66 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clkc 0xa7>;
+        resets = <&rstc 0x97>;
+        phy-handle1 = <&eth_phy0>;
+        phy-handle2 = <&eth_phy1>;
+        pinctrl-0 = <&emac_demo_board_v3_pins>;
+        pinctrl-names = "default";
+        nvmem-cells = <&mac_addr0>, <&mac_addr1>;
+        nvmem-cell-names = "mac_addr0", "mac_addr1";
+
Quoted 8 lines

Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml line 144

Quoted 6 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +6 -0
+        mdio {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            eth_phy0: ethernet-phy@0 {
+                reg = <0>;
+                phy-mode = "rmii";

This is in the wrong place. It is a MAC property. You usually put it next to phy-handle.

Yes, I'll move phy-mode to Ethernet-port next patch.

Quoted 19 lines

Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml line 150

Quoted 6 lines
Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml new file +6 -0
+            };
+            eth_phy1: ethernet-phy@1 {
+                reg = <1>;
+                phy-mode = "rmii";
+            };
+        };

I would suggest you structure this differently to make it clear it is a two port switch:

	ethernet-ports {
		#address-cells = <1>;
                #size-cells = <0>;

                port@0 {
                    reg = <0>;
		    phy-handle = <&eth_phy0>;
		    phy-mode = "rmii";
		}
Quoted 8 lines
		port@1 {
                    reg = <1>;
		    phy-handle = <&eth_phy1>;
		    phy-mode = "rmii";
		}
	}

	Andrew

Yes, refer to new example:

examples:
- |

    #include <dt-bindings/interrupt-controller/irq.h>
    emac: emac@9c108000 {
        compatible = "sunplus,sp7021-emac";
        reg = <0x9c108000 0x400>, <0x9c000280 0x80>;
        reg-names = "emac", "moon5";
        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";
        nvmem-cells = <&mac_addr0>, <&mac_addr1>;
        nvmem-cell-names = "mac_addr0", "mac_addr1";

        ethernet-ports {
            #address-cells = <1>;
            #size-cells = <0>;

            port@0 {
                reg = <0>;
                phy-handle = <&eth_phy0>;
                phy-mode = "rmii";
            };

            port@1 {
                reg = <1>;
                phy-handle = <&eth_phy1>;
                phy-mode = "rmii";
            };
        };

        mdio {
            #address-cells = <1>;
            #size-cells = <0>;

            eth_phy0: ethernet-phy@0 {
                reg = <0>;
            };

            eth_phy1: ethernet-phy@1 {
                reg = <1>;
            };
        };
    };

Is it correct?

Thank you very much for your review.

author
#39 lore raw

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

In reply to Wells Lu

Add driver for Sunplus SP7021.

---
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
MAINTAINERS +1 -0
index 737b9d0..ec1ddb1 100644
@@ -18006,6 +18006,7 @@ L:	[email protected]
 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 <[email protected]>
drivers/net/ethernet/Kconfig +1 -0
index 412ae3e..0084852 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index aaa5078..e4ce162 100644
@@ -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/
drivers/net/ethernet/sunplus/Kconfig new file +36 -0
new file mode 100644
index 0000000..5af2c5b
@@ -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
drivers/net/ethernet/sunplus/Makefile new file +6 -0
new file mode 100644
index 0000000..963ba1d
@@ -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
drivers/net/ethernet/sunplus/sp_define.h new file +212 -0

212 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_desc.c new file +231 -0

231 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_desc.h new file +21 -0
new file mode 100644
index 0000000..20f7519
@@ -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
drivers/net/ethernet/sunplus/sp_driver.c new file +606 -0

606 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_driver.h new file +23 -0
new file mode 100644
index 0000000..ea80a9e
@@ -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
drivers/net/ethernet/sunplus/sp_hal.c new file +331 -0

331 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_hal.h new file +31 -0
new file mode 100644
index 0000000..f4b1979
@@ -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
drivers/net/ethernet/sunplus/sp_int.c new file +286 -0

286 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_int.h new file +13 -0
new file mode 100644
index 0000000..7de8df4
@@ -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
drivers/net/ethernet/sunplus/sp_mac.c new file +63 -0

63 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_mac.h new file +23 -0
new file mode 100644
index 0000000..f35f3b7
@@ -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
drivers/net/ethernet/sunplus/sp_mdio.c new file +90 -0

90 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_mdio.h new file +20 -0
new file mode 100644
index 0000000..d708624
@@ -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
drivers/net/ethernet/sunplus/sp_phy.c new file +64 -0

64 lines not on this page · raw message

drivers/net/ethernet/sunplus/sp_phy.h new file +16 -0
new file mode 100644
index 0000000..4ae0351
@@ -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
drivers/net/ethernet/sunplus/sp_register.h new file +96 -0

96 lines not on this page · raw message

Signature · 1 line
2.7.4
#40 lore raw

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

11/11/21 12:04 PM, Wells Lu пишет:

Quoted 183 lines

Add driver for Sunplus SP7021.

---
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

MAINTAINERS +1 -0
index 737b9d0..ec1ddb1 100644
@@ -18006,6 +18006,7 @@ L:	[email protected]
  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 <[email protected]>
drivers/net/ethernet/Kconfig +1 -0
index 412ae3e..0084852 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index aaa5078..e4ce162 100644
@@ -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/
drivers/net/ethernet/sunplus/Kconfig new file +36 -0
new file mode 100644
index 0000000..5af2c5b
@@ -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
drivers/net/ethernet/sunplus/Makefile new file +6 -0
new file mode 100644
index 0000000..963ba1d
@@ -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
drivers/net/ethernet/sunplus/sp_define.h new file +24 -0
new file mode 100644
index 0000000..40e15ba
@@ -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>

drivers/net/ethernet/sunplus/sp_define.h line 34

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_define.h new file +10 -0
+#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 /* */

Quoted 324 lines
drivers/net/ethernet/sunplus/sp_define.h new file +178 -0
+#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
drivers/net/ethernet/sunplus/sp_desc.c new file +140 -0
new file mode 100644
index 0000000..fed91ec
@@ -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);

drivers/net/ethernet/sunplus/sp_desc.c line 150

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +10 -0
+			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

drivers/net/ethernet/sunplus/sp_desc.c line 163

Quoted 13 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +13 -0
+			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

Quoted 160 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +68 -0
+	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);
+}
drivers/net/ethernet/sunplus/sp_desc.h new file +21 -0
new file mode 100644
index 0000000..20f7519
@@ -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
drivers/net/ethernet/sunplus/sp_driver.c new file +59 -0
new file mode 100644
index 0000000..a1c76b9
@@ -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;

drivers/net/ethernet/sunplus/sp_driver.c line 69

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+	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?

Quoted 21 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +21 -0
+	}
+
+	/* 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;
+			}
+		}

drivers/net/ethernet/sunplus/sp_driver.c line 100

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+	}
+
+	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

Quoted 69 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +69 -0
+	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:

drivers/net/ethernet/sunplus/sp_driver.c line 179

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+	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?

Quoted 71 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +71 -0
+
+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;

drivers/net/ethernet/sunplus/sp_driver.c line 260

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+	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

Quoted 404 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +346 -0
+	}
+	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 <[email protected]>");
+MODULE_DESCRIPTION("Sunplus Dual 10M/100M Ethernet driver");
+MODULE_LICENSE("GPL v2");
drivers/net/ethernet/sunplus/sp_driver.h new file +23 -0
new file mode 100644
index 0000000..ea80a9e
@@ -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
drivers/net/ethernet/sunplus/sp_hal.c new file +23 -0
new file mode 100644
index 0000000..a7f06d7
@@ -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);
+	}

drivers/net/ethernet/sunplus/sp_hal.c line 33

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+
+	// 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

Quoted 123 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +123 -0
+
+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);
+	}

drivers/net/ethernet/sunplus/sp_hal.c line 166

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+}
+
+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

Quoted 13 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +13 -0
+
+	// 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);

drivers/net/ethernet/sunplus/sp_hal.c line 189

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+
+	// 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

drivers/net/ethernet/sunplus/sp_hal.c line 195

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+
+	// 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

drivers/net/ethernet/sunplus/sp_hal.c line 200

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+	       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

Quoted 284 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +131 -0
+
+	// 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);
+}
drivers/net/ethernet/sunplus/sp_hal.h new file +31 -0
new file mode 100644
index 0000000..f4b1979
@@ -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
drivers/net/ethernet/sunplus/sp_int.c new file +110 -0
new file mode 100644
index 0000000..27d81b6
@@ -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);
+

drivers/net/ethernet/sunplus/sp_int.c line 120

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_int.c new file +10 -0
+			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

Quoted 600 lines
drivers/net/ethernet/sunplus/sp_int.c new file +166 -0
+			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;
+}
drivers/net/ethernet/sunplus/sp_int.h new file +13 -0
new file mode 100644
index 0000000..7de8df4
@@ -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
drivers/net/ethernet/sunplus/sp_mac.c new file +63 -0
new file mode 100644
index 0000000..6a3dfb1
@@ -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);
+		}
+	}
+}
drivers/net/ethernet/sunplus/sp_mac.h new file +23 -0
new file mode 100644
index 0000000..f35f3b7
@@ -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
drivers/net/ethernet/sunplus/sp_mdio.c new file +90 -0
new file mode 100644
index 0000000..f6a7e64
@@ -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;
+	}
+}
drivers/net/ethernet/sunplus/sp_mdio.h new file +20 -0
new file mode 100644
index 0000000..d708624
@@ -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
drivers/net/ethernet/sunplus/sp_phy.c new file +64 -0
new file mode 100644
index 0000000..df6df3a
@@ -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;
+	}
+}
drivers/net/ethernet/sunplus/sp_phy.h new file +16 -0
new file mode 100644
index 0000000..4ae0351
@@ -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
drivers/net/ethernet/sunplus/sp_register.h new file +96 -0
new file mode 100644
index 0000000..690c0dd
@@ -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
#41 lore raw

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

Hi,

Quoted 187 lines

11/11/21 12:04 PM, Wells Lu пишет:

Quoted 186 lines

Add driver for Sunplus SP7021.

---
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

MAINTAINERS +1 -0
index 737b9d0..ec1ddb1 100644
@@ -18006,6 +18006,7 @@ L:	[email protected]
  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 <[email protected]>
drivers/net/ethernet/Kconfig +1 -0
index 412ae3e..0084852 100644
@@ -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"
drivers/net/ethernet/Makefile +1 -0
index aaa5078..e4ce162 100644
@@ -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/
drivers/net/ethernet/sunplus/Kconfig new file +36 -0
new file mode 100644
index 0000000..5af2c5b
@@ -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
drivers/net/ethernet/sunplus/Makefile new file +6 -0
new file mode 100644
index 0000000..963ba1d
@@ -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
drivers/net/ethernet/sunplus/sp_define.h new file +27 -0
new file mode 100644
index 0000000..40e15ba
@@ -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>
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_define.h line 34

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_define.h new file +7 -0
+#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?

Quoted 328 lines
Quoted 17 lines
drivers/net/ethernet/sunplus/sp_define.h new file +17 -0
+#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)

drivers/net/ethernet/sunplus/sp_define.h

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_define.h new file +10 -0
+#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 | \

Quoted 300 lines
drivers/net/ethernet/sunplus/sp_define.h new file +151 -0
+					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
drivers/net/ethernet/sunplus/sp_desc.c new file +143 -0
new file mode 100644
index 0000000..fed91ec
@@ -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;
+
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c line 150

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +7 -0
+			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;
}
Quoted 6 lines
Quoted 6 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -0
+			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.
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c line 163

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +7 -0
+			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.

Quoted 163 lines
Quoted 163 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +68 -0
+	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);
+}
drivers/net/ethernet/sunplus/sp_desc.h new file +21 -0
new file mode 100644
index 0000000..20f7519
@@ -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
drivers/net/ethernet/sunplus/sp_driver.c new file +62 -0
new file mode 100644
index 0000000..a1c76b9
@@ -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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 69

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +7 -0
+	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?

Quoted 24 lines
Quoted 24 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +24 -0
+	}
+
+	/* 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);
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 100

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +7 -0
+	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;
}
Quoted 72 lines
Quoted 72 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +72 -0
+	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);
+	}
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 179

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +7 -0
+
+	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?

Quoted 73 lines
Quoted 73 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +73 -0
+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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 260

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +7 -0
+	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;

?

Quoted 408 lines
Quoted 360 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +346 -0
+	}
+	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 <[email protected]>");
+MODULE_DESCRIPTION("Sunplus Dual 10M/100M Ethernet driver");
+MODULE_LICENSE("GPL v2");
drivers/net/ethernet/sunplus/sp_driver.h new file +8 -0
new file mode 100644
index 0000000..ea80a9e
@@ -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__
+

drivers/net/ethernet/sunplus/sp_driver.h line 18

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.h new file +10 -0
+#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)

Quoted 37 lines
drivers/net/ethernet/sunplus/sp_driver.h new file +5 -0
+
+#define RX_NAPI_WEIGHT          16
+#define TX_NAPI_WEIGHT          16
+
+#endif
drivers/net/ethernet/sunplus/sp_hal.c new file +26 -0
new file mode 100644
index 0000000..a7f06d7
@@ -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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 33

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +7 -0
+	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.

Quoted 127 lines
Quoted 85 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +85 -0
+
+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 {

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+			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",

Quoted 31 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +31 -0
+			   (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)
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 166

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +7 -0
+{
+	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);

Quoted 15 lines
Quoted 15 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +15 -0
+	// 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);
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 189

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +7 -0
+
+	// 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);

Quoted 6 lines

drivers/net/ethernet/sunplus/sp_hal.c line 195

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+	// 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);

Quoted 6 lines

drivers/net/ethernet/sunplus/sp_hal.c line 200

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+	       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);

Quoted 286 lines
Quoted 286 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +130 -0
+	// 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);
+}
drivers/net/ethernet/sunplus/sp_hal.h new file +31 -0
new file mode 100644
index 0000000..f4b1979
@@ -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
drivers/net/ethernet/sunplus/sp_int.c new file +113 -0
new file mode 100644
index 0000000..27d81b6
@@ -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;
+
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_int.c line 120

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_int.c new file +7 -0
+			/*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);
Quoted 592 lines
Quoted 592 lines
drivers/net/ethernet/sunplus/sp_int.c new file +166 -0
+			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;
+}
drivers/net/ethernet/sunplus/sp_int.h new file +13 -0
new file mode 100644
index 0000000..7de8df4
@@ -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
drivers/net/ethernet/sunplus/sp_mac.c new file +63 -0
new file mode 100644
index 0000000..6a3dfb1
@@ -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);
+		}
+	}
+}
drivers/net/ethernet/sunplus/sp_mac.h new file +23 -0
new file mode 100644
index 0000000..f35f3b7
@@ -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
drivers/net/ethernet/sunplus/sp_mdio.c new file +90 -0
new file mode 100644
index 0000000..f6a7e64
@@ -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;
+	}
+}
drivers/net/ethernet/sunplus/sp_mdio.h new file +20 -0
new file mode 100644
index 0000000..d708624
@@ -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
drivers/net/ethernet/sunplus/sp_phy.c new file +64 -0
new file mode 100644
index 0000000..df6df3a
@@ -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;
+	}
+}
drivers/net/ethernet/sunplus/sp_phy.h new file +16 -0
new file mode 100644
index 0000000..4ae0351
@@ -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
drivers/net/ethernet/sunplus/sp_register.h new file +89 -0
new file mode 100644
index 0000000..690c0dd
@@ -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
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_register.h line 96

Quoted 8 lines
drivers/net/ethernet/sunplus/sp_register.h new file +7 -0
+#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.

#42 lore raw

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

Quoted 10 lines
Quoted 2 lines
Quoted 1 line

+//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 */

Yes. The Kernel is written in C, so C style comments are preferred over C++ comments, even if later versions of the C standard allow C++ style comments.

You should also read the netdev FAQ, which makes some specific comments about how multi-line comments should be formatted.

Quoted 7 lines

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;

If it is clear how to fix the code, just do it. No need to tell us what you are going to do, we will see the change when reviewing the next version.

Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +9 -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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 69

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +7 -0
+	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?

Quoted 5 lines

(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?

There are a few hundred examples to follow, other MAC drivers. What do they do when out of TX buffers? Find the most common pattern, and follow it.

You should also thinking about the netdev_info(). Do you really want to spam the kernel log? Say you are connected to a 10/Half link, and the application is trying to send UDP at 100Mbps, Won't you see a lot of these messages? change it to _debug(), or rate limit it.

Quoted 30 lines

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);
}
Quoted 1 line

Is that ok?

This ndev2 stuff is not nice. You probably need a cleaner abstract of two netdev's sharing one TX and RX ring. See if there are any other switchdev drivers with a similar structure you can copy. Maybe cpsw_new.c? But be careful with that driver. cpsw is a bit of a mess due to an incorrect initial design with respect to its L2 switch. A lot of my initial comments are to stop you making the same mistakes.

Andrew

#43 lore raw

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

Hi,

Quoted 5 lines
Quoted 5 lines
Quoted 2 lines
Quoted 1 line

+//define MAC interrupt status bit

please embrace all comments with /* */

Do you mean to modify comment, for example,

Quoted 8 lines
Quoted 5 lines

//define MAC interrupt status bit

to

/* define MAC interrupt status bit */

Yes. The Kernel is written in C, so C style comments are preferred over C++ comments, even if later versions of the C standard allow C++ style comments.

I'll modify all comments to C style in next patch.

Quoted 2 lines

You should also read the netdev FAQ, which makes some specific comments about how multi-line comments should be formatted.

Thanks for routing me to the document.
I'll use the new format for multi-line comments.
---
It is requested that you make it look like this:

/* foobar blah blah blah
* another line of text
*/

Quoted 10 lines
Quoted 7 lines

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;

If it is clear how to fix the code, just do it. No need to tell us what you are going to do, we will see the change when reviewing the next version.

Thanks, I see.

Quoted 18 lines
Quoted 18 lines
Quoted 17 lines
Quoted 6 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +6 -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;

drivers/net/ethernet/sunplus/sp_driver.c line 69

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+	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?

Quoted 8 lines
Quoted 5 lines

(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?

There are a few hundred examples to follow, other MAC drivers. What do they do when out of TX buffers? Find the most common pattern, and follow it.

But some drivers return NETDEV_TX_BUSY, some drivers drop packet and return NETDEV_TX_OK Some drivers seem do not take care this issue. I am not sure.

Quoted 4 lines

You should also thinking about the netdev_info(). Do you really want to spam the kernel log? Say you are connected to a 10/Half link, and the application is trying to send UDP at 100Mbps, Won't you see a lot of these messages? change it to _debug(), or rate limit it.

Yes, I'll modify most netdev_info() to netdev_dbg() in next patch.

Quoted 29 lines
Quoted 29 lines

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); }
Quoted 7 lines
Quoted 1 line

Is that ok?

This ndev2 stuff is not nice. You probably need a cleaner abstract of two netdev's sharing one TX and RX ring. See if there are any other switchdev drivers with a similar structure you can copy. Maybe cpsw_new.c? But be careful with that driver. cpsw is a bit of a mess due to an incorrect initial design with respect to its L2 switch. A lot of my initial comments are to stop you making the same mistakes.

I'll define a array (pointer to struct net_dev) in driver private (shared) structure to access to all net devices. No more mac->next_ndev;.

Quoted 1 line

Andrew

Thank you very much for your review.

Best regards,
Wells

#44 lore raw

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

In reply to Wells Lu
I love your patch! Yet something to improve:

Hi Wells,

I love your patch! Yet something to improve:

[auto build test ERROR on net-next/master]
[also build test ERROR on net/master robh/for-next linus/master v5.15 next-20211112] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Wells-Lu/devicetree-bindings-net-Add-bindings-doc-for-Sunplus-SP7021/20211111-180647
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git cc0356d6a02e064387c16a83cb96fe43ef33181e
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 11.2.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/67fef21f20f9dd81655eb490c5fa41c075f4af3d
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Wells-Lu/devicetree-bindings-net-Add-bindings-doc-for-Sunplus-SP7021/20211111-180647 git checkout 67fef21f20f9dd81655eb490c5fa41c075f4af3d
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=m68k

If you fix the issue, kindly add following tag as appropriate

All errors (new ones prefixed by >>):

>> drivers/net/ethernet/sunplus/sp_driver.c:200:7: error: no previous prototype for 'sp7021_otp_read_mac' [-Werror=missing-prototypes]
     200 | char *sp7021_otp_read_mac(struct device *dev, ssize_t *len, char *name)
         |       ^~~~~~~~~~~~~~~~~~~

cc1: all warnings being treated as errors

vim +/sp7021_otp_read_mac +200 drivers/net/ethernet/sunplus/sp_driver.c

199

 > 200	char *sp7021_otp_read_mac(struct device *dev, ssize_t *len, char *name)

201 {
202 char *ret = NULL;
203 struct nvmem_cell *cell = nvmem_cell_get(dev, name);
204
205 if (IS_ERR_OR_NULL(cell)) {
206 dev_err(dev, "OTP %s read failure: %ld", name, PTR_ERR(cell));
207 return NULL;
208 }
209
210 ret = nvmem_cell_read(cell, len);
211 nvmem_cell_put(cell);
212 dev_dbg(dev, "%zd bytes are read from OTP %s.", *len, name);
213
214 return ret;
215 }
216

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#45 lore raw

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

In reply to Wells Lu
Quoted 4 lines · Wells Lu

Add driver for Sunplus SP7021.

---

[snip]

Quoted 11 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +11 -0
+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;
+}
+

drivers/net/ethernet/sunplus/sp_mdio.c line 28

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +10 -0
+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;
+}

You should not be exposing these functions, if you do, that means another part of your code performs MDIO bus read/write operations without using the appropriate layer, so no.

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +6 -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);

drivers/net/ethernet/sunplus/sp_mdio.c line 44

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +10 -0
+}
+
+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)

Those function names need to be prefixed with sp_ to denote the driver local scope, this applies for your entire patch set.

[snip]

Quoted 12 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +6 -0
new file mode 100644
index 0000000..d708624
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+
+#ifndef __SP_MDIO_H__

drivers/net/ethernet/sunplus/sp_mdio.h line 16

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +10 -0
+#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);

Please scope your functions better, and name them sp_mdio_read, etc. because mdio_read() is way too generic. Also, can you please follow the same prototype as what include/linux/mdio.h has for the mdiobus->read and ->write calls, that is phy_id is int, regnum is u32, etc.

Quoted 11 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +4 -0
+u32  mdio_init(struct platform_device *pdev, struct net_device *ndev);
+void mdio_remove(struct net_device *ndev);
+
+#endif
drivers/net/ethernet/sunplus/sp_phy.c new file +1 -0
new file mode 100644
index 0000000..df6df3a
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0

drivers/net/ethernet/sunplus/sp_phy.c line 11

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +10 -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)
+{
+}

Does your MAC fully auto-configure based on the PHY's link parameters, if so, how does it do it? You most certainly need to act on duplex changes, or speed changes no?

Quoted 14 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +14 -0
+
+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;
+	}
+

drivers/net/ethernet/sunplus/sp_phy.c line 35

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +10 -0
+	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));

This is a layering violation, and you should not be doing those things here, if you need to advertise flow control, then please set ADVERTISE_PAUSE_CAP and/or ADVERTISE_PAUSE_ASYM accordingly, see whether phy_set_asym_pause() can do what you need it to.

Quoted 18 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +18 -0
+
+	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);

drivers/net/ethernet/sunplus/sp_phy.c line 63

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +10 -0
+}
+
+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;
+	}

The net_device structure already contains a phy_device pointer, you don't need to have one in your sp_mac structure, too.

Signature · 1 line
Florian
#46 lore raw

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

Hi Florian

You are basically pointing out issues i already pointed out in previous versions, and have been ignored :-(

Wells, please look at the comments i made on your earlier versions. Those comments are still valid and need addressing.

Andrew

#47 lore raw

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

Hi Andrew,

Thank you for reminding.

I'll do my best to address all comments.
I fully understand that all reviewers' comments should be addressed. If I lost addressing any comments, please kindly remind me again.

Best regards,
Wells

Quoted 9 lines

Hi Florian

You are basically pointing out issues i already pointed out in previous versions, and have been ignored :-(

Wells, please look at the comments i made on your earlier versions. Those comments are still valid and need addressing.

Andrew

#48 lore raw

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

In reply to Florian Fainelli

Hi,

Quoted 22 lines · Wells Lu
Quoted 4 lines

Add driver for Sunplus SP7021.

---

[snip]

Quoted 14 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +14 -0
+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);
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_mdio.c line 28

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +5 -0
+	if (ret < 0)
+		return -EOPNOTSUPP;
+
+	return 0;
+}

You should not be exposing these functions, if you do, that means another part of your code performs MDIO bus read/write operations without using the appropriate layer, so no.

Yes, I'll re-declare the two functions as static functions.

Quoted 8 lines
Quoted 8 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +8 -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) {
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_mdio.c line 44

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +5 -0
+	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)

Those function names need to be prefixed with sp_ to denote the driver local scope, this applies for your entire patch set.

Yes, I'll add vendor-specified prefix to the two functions and all the other functions in the drivers.

Quoted 22 lines

[snip]

Quoted 20 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +13 -0
new file mode 100644
index 0000000..d708624
@@ -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
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_mdio.h line 16

Quoted 3 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +3 -0
+
+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);

Please scope your functions better, and name them sp_mdio_read, etc. because mdio_read() is way too generic. Also, can you please follow the same prototype as what include/linux/mdio.h has for the mdiobus->read and ->write calls, that is phy_id is int, regnum is u32, etc.

Yes, I'll re-declare the two functions, mdio_read() and mdio_write(), as static functions and add vendor-specified prefix to them.

The wrong declaration of the two functions will be removed from the header file.

Quoted 15 lines
Quoted 15 lines
drivers/net/ethernet/sunplus/sp_mdio.h new file +4 -0
+u32  mdio_init(struct platform_device *pdev, struct net_device
+*ndev); void mdio_remove(struct net_device *ndev);
+
+#endif
drivers/net/ethernet/sunplus/sp_phy.c new file +4 -0
new file mode 100644
index 0000000..df6df3a
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_phy.c

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +5 -0
+
+#include "sp_phy.h"
+#include "sp_mdio.h"
+
+static void mii_linkchange(struct net_device *netdev) { }

Does your MAC fully auto-configure based on the PHY's link parameters, if so, how does it do it? You most certainly need to act on duplex changes, or speed changes no?

Yes, it does. SP7021 MAC communicates with PHY automatically. It reads link status (half- or full-duplex, 10M or 100M) from PHY and sets itself automatically.
It also reads port status (link up or down) and generates interrupt to driver.

Quoted 21 lines
Quoted 21 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +21 -0
+
+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
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_phy.c line 33

Quoted 3 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +3 -0
+	// 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));

This is a layering violation, and you should not be doing those things here, if you need to advertise flow control, then please set ADVERTISE_PAUSE_CAP and/or ADVERTISE_PAUSE_ASYM accordingly, see whether
phy_set_asym_pause() can do what you need it to.

Yes, I'll remove the statement, instead, use phy_set_asym_pause().

Quoted 20 lines
Quoted 20 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +20 -0
+
+	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);
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_phy.c line 63

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_phy.c new file +5 -0
+
+	if (mac->phy_dev) {
+		phy_disconnect(mac->phy_dev);
+		mac->phy_dev = NULL;
+	}

The net_device structure already contains a phy_device pointer, you don't need to have one in your sp_mac structure, too.

Yes, I'll remove phy_device from struct sp_mac.

Quoted 2 lines

--
Florian

Thank you very much for your review.

#49 lore raw

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

In reply to Wells Lu
Quoted 1 line

+void rx_descs_flush(struct sp_common *comm)

As both Florian and I have said, you need a prefix for all your functions, structures, etc. sp_ is not the best prefix either, it is not very unique. spl2sw_ would be better.

drivers/net/ethernet/sunplus/sp_desc.c line 64

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +5 -0
+void rx_descs_clean(struct sp_common *comm)
+{
+	u32 i, j;
+	struct mac_desc *rx_desc;
+	struct skb_info *rx_skbinfo;

netdev wants reverse christmas tree. You need to change the order of your local variables, longest lines first, shorted last.

Quoted 58 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +57 -0
+
+	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;
+	}
+}
 
+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);
+			rx_desc[j].addr1 = rx_skbinfo[j].mapping;
+			rx_desc[j].addr2 = 0;
+			rx_desc[j].cmd2 = (j == comm->rx_desc_num[i] - 1) ?

drivers/net/ethernet/sunplus/sp_desc.c line 163

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +10 -0
+					  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:

lower case labels. Didn't somebody already say that?

drivers/net/ethernet/sunplus/sp_desc.c line 204

Quoted 9 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +9 -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

Avoid #if statements. Why is this needed?

Quoted 6 lines

+++ b/drivers/net/ethernet/sunplus/sp_driver.c

drivers/net/ethernet/sunplus/sp_driver.c new file +4 -0
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */

drivers/net/ethernet/sunplus/sp_driver.c line 14

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +10 -0
+
+#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

This does not have the locally administered bit set. Should it? Or is this and address from your OUI?

drivers/net/ethernet/sunplus/sp_driver.c line 130

Quoted 3 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +3 -0
+static void ethernet_set_rx_mode(struct net_device *ndev)
+{
+	if (ndev) {

How can ndev be NULL?

drivers/net/ethernet/sunplus/sp_hal.c line 9

Quoted 11 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+++ 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)

I suggest you avoid any references to hal. It makes people think you have ported a driver from some other operating system and then put a layer of code on top of it. That is not how you do it in Linux. This is a Linux driver, nothing else.

drivers/net/ethernet/sunplus/sp_hal.c line 34

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +4 -0
+void hal_mac_reset(struct sp_mac *mac)
+{
+}
+

Should not exist.

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +4 -0
+void hal_mac_addr_set(struct sp_mac *mac)
+{
+	struct sp_common *comm = mac->comm;
+	u32 reg;

drivers/net/ethernet/sunplus/sp_hal.c line 62

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+
+	// 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);

Is this actually adding an entry into the address translation table? If so, make this clear in the function name. You are not setting the MAC address, you are just adding a static forwarding entry.

Quoted 29 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +28 -0
+
+	// 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_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)) ||

drivers/net/ethernet/sunplus/sp_hal.c line 245

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+		   (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));

This looks like it belongs in the ethtool code.

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+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;
+

drivers/net/ethernet/sunplus/sp_hal.c line 262

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+	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;
+}

Should go with the other mdio code.

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+void hal_phy_addr(struct sp_mac *mac)
+{
+	struct sp_common *comm = mac->comm;
+	u32 reg;
+
+	// Set address of phy.

drivers/net/ethernet/sunplus/sp_hal.c line 296

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +10 -0
+	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);
+}

As i said before, the hardware never directly communicates with the PHY. So you can remove this.

drivers/net/ethernet/sunplus/sp_int.c line 18

Quoted 8 lines
drivers/net/ethernet/sunplus/sp_int.c new file +8 -0
+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);

phylib should be handling the carrier for you.

drivers/net/ethernet/sunplus/sp_int.c line 34

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_int.c new file +10 -0
+	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);
+		}

Looks very odd. The two netdev should be independent.

Quoted 28 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +22 -0
new file mode 100644
index 0000000..f6a7e64
@@ -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;
+

drivers/net/ethernet/sunplus/sp_mdio.c line 32

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +10 -0
+	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;

What happened about my request to return -EOPNOTSUPP for C45 requests?

Andrew

#50 lore raw

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

Hi,

Quoted 4 lines
Quoted 1 line

+void rx_descs_flush(struct sp_common *comm)

As both Florian and I have said, you need a prefix for all your functions, structures, etc. sp_ is not the best prefix either, it is not very unique. spl2sw_ would be better.

I'll add prefix spl2sw for all functions, structures, file-names in next patch.

I thought comment for revising prefix is only for structures, function and file name with prefix l2sw_ because 'l2sw_' has been used by other modules.

Now I know prefix is necessary for all in this driver, except local variables and structure members.

Quoted 7 lines

drivers/net/ethernet/sunplus/sp_desc.c line 13

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +4 -0
+void rx_descs_clean(struct sp_common *comm) {
+	u32 i, j;
+	struct mac_desc *rx_desc;
+	struct skb_info *rx_skbinfo;

netdev wants reverse christmas tree. You need to change the order of your local variables, longest lines first, shorted last.

Yes, I'll rearrange local variables to 'reverse Christmas tree' order in next patch.

Quoted 61 lines
Quoted 61 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +60 -0
+
+	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;
+	}
+}
 
+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);
+			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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c line 163

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -0
+		}
+	}
+
+	return 0;
+
+MEM_ALLOC_FAIL:

lower case labels. Didn't somebody already say that?

I'll modify all labels to lowercase in next patch. Yes, Denis said that but patch not yet sent out.

Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -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

Avoid #if statements. Why is this needed?

Yes, I'll remove the #if statement in next patch.
It is indeed not necessary.
RX_DESC_QUEUE_NUM is equal to 2.

Quoted 11 lines
Quoted 11 lines

+++ b/drivers/net/ethernet/sunplus/sp_driver.c

drivers/net/ethernet/sunplus/sp_driver.c new file +9 -0
@@ -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>
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 14

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +5 -0
+#include "sp_driver.h"
+#include "sp_phy.h"
+
+static const char def_mac_addr[ETHERNET_MAC_ADDR_LEN] = {
+	0xfc, 0x4b, 0xbc, 0x00, 0x00, 0x00

This does not have the locally administered bit set. Should it? Or is this and address from your OUI?

This is default MAC address when MAC address in NVMEM is not found. Fc:4b:bc:00:00:00 is OUI of "Sunplus Technology Co., Ltd.". Can I keep this? or it should be removed?

Quoted 4 lines

Review line 16

Quoted 2 lines
(quoted diff) +2 -0
+static void ethernet_set_rx_mode(struct net_device *ndev) {
+	if (ndev) {

How can ndev be NULL?

Yes, I'll remove 'if (ndev) {' statement in next patch. It is redundant.

Quoted 7 lines
Quoted 7 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -0
+++ 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.
+ */
+
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 9

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +4 -0
+#include <linux/iopoll.h>
+#include "sp_hal.h"
+
+void hal_mac_stop(struct sp_mac *mac)

I suggest you avoid any references to hal. It makes people think you have ported a driver from some other operating system and then put a layer of code on top of it. That is not how you do it in Linux. This is a Linux driver, nothing else.

Yes, I'll change file name 'sp_hal.c' to 'spl2sw_hw.c'. Function name in this file will also be changed, for example:
hal_mac_stop() --> spl2sw_hw_mac_stop()

Quoted 4 lines

Review line 11

Quoted 2 lines
(quoted diff) +2 -0
+void hal_mac_reset(struct sp_mac *mac) { }
+

Should not exist.

Yes, I'll remove it in next patch.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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);
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 62

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +4 -0
+
+	// Set aging=1
+	writel((mac->cpu_port << 10) + (mac->vlan_id << 7) + (1 << 4) + 0x1,
+	       comm->sp_reg_base + SP_WT_MAC_AD0);

Is this actually adding an entry into the address translation table?
If so, make this clear in the function name. You are not setting the MAC address, you are just adding a static forwarding entry.

Yes, this is actually adding an entry into address table. I'll change function name to spl2sw_mac_add_addr() in next patch. Is the name ok?

Quoted 32 lines
Quoted 32 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +31 -0
+
+	// 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_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
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+		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));

This looks like it belongs in the ethtool code.

This function sets receiving mode.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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,
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 262

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+				comm->sp_reg_base + SP_PHY_CNTL_REG1);
+	if (ret == 0)
+		return val >> 16;
+	else
+		return ret;
+}

Should go with the other mdio code.

I'll move it into 'spl2sw_mdio.c' in next patch.

I put all hardware-related functions in sp_hal.c (will be changed to spl2sw_hw.c). All functions in other files won't touch hardware registers.
This seems not Linux driver style.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+		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); }

As i said before, the hardware never directly communicates with the PHY. So you can remove this.

I'll remove this function in next patch.

But now I cannot find a way to disable hardware 'auto rmii' function. If I remove this function right now, MAC may get wrong status of PHY from wrong address because SP7021 MAC communicates with PHY automatically. This may cause more problem.

I am consulting with ASIC engineer. Hopefully, someone can find a way to disable the auto function.

Quoted 9 lines

drivers/net/ethernet/sunplus/sp_int.c line 18

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_int.c new file +7 -0
+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);

phylib should be handling the carrier for you.

I'll remove this function in next patch.
If 'auto rmii' function is removed, we no more need this function.

Quoted 4 lines
Quoted 4 lines
drivers/net/ethernet/sunplus/sp_int.c new file +4 -0
+	if (mac->next_ndev) {
+		struct net_device *ndev2 = mac->next_ndev;
+
+		if (!netif_carrier_ok(ndev2) && (reg & PORT_ABILITY_LINK_ST_P1)) {
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_int.c line 34

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_int.c new file +6 -0
+			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);
+		}

Looks very odd. The two netdev should be independent.

I don't understand your comment.
ndev checks PORT_ABILITY_LINK_ST_P0
ndev2 checks PORT_ABILITY_LINK_ST_P1
They are independent already.

Quoted 30 lines
Quoted 30 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +23 -0
new file mode 100644
index 0000000..f6a7e64
@@ -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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_mdio.c line 32

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +6 -0
+
+	return 0;
+}
+
+static int mii_read(struct mii_bus *bus, int phy_id, int regnum) {
+	struct sp_mac *mac = bus->priv;

What happened about my request to return -EOPNOTSUPP for C45 requests?

Sorry for overlooking the comment!
I am not sure how to check C45 request. Should I add statements like:

	if (regnum & MII_ADDR_C45)
		Return -EOPNOTSUPP;

for mdio_read() and mdio_write()?

Quoted 1 line

Andrew

Thank you very much for your review!

Best regards,
Wells

#51 lore raw

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

Quoted 9 lines
Quoted 5 lines

drivers/net/ethernet/sunplus/sp_driver.c line 14

Quoted 2 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +2 -0
+static const char def_mac_addr[ETHERNET_MAC_ADDR_LEN] = {
+	0xfc, 0x4b, 0xbc, 0x00, 0x00, 0x00

This does not have the locally administered bit set. Should it? Or is this and address from your OUI?

This is default MAC address when MAC address in NVMEM is not found. Fc:4b:bc:00:00:00 is OUI of "Sunplus Technology Co., Ltd.". Can I keep this? or it should be removed?

Please add a comment about whos OUI it is.

It is however more normal to use a random MAC address if no other MAC address is available. That way, you avoid multiple devices on one LAN using the same default MAC address.

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/net/ethernet/sunplus/sp_int.c new file +4 -0
+	if (mac->next_ndev) {
+		struct net_device *ndev2 = mac->next_ndev;
+
+		if (!netif_carrier_ok(ndev2) && (reg & PORT_ABILITY_LINK_ST_P1)) {
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_int.c line 34

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_int.c new file +6 -0
+			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);
+		}

Looks very odd. The two netdev should be independent.

Quoted 4 lines

I don't understand your comment.
ndev checks PORT_ABILITY_LINK_ST_P0
ndev2 checks PORT_ABILITY_LINK_ST_P1
They are independent already.

I would try to remove the mac->next_ndev. I think without that, you will get a cleaner abstraction. You might want to keep an array of mac pointers in your top level shared structure.

Andrew

#52 lore raw

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

Hi,

Quoted 6 lines
Quoted 6 lines
Quoted 5 lines

drivers/net/ethernet/sunplus/sp_driver.c line 14

Quoted 2 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +2 -0
+static const char def_mac_addr[ETHERNET_MAC_ADDR_LEN] = {
+	0xfc, 0x4b, 0xbc, 0x00, 0x00, 0x00

This does not have the locally administered bit set. Should it? Or is this and address from your OUI?

Quoted 8 lines
Quoted 3 lines

This is default MAC address when MAC address in NVMEM is not found. Fc:4b:bc:00:00:00 is OUI of "Sunplus Technology Co., Ltd.". Can I keep this? or it should be removed?

Please add a comment about whos OUI it is.

It is however more normal to use a random MAC address if no other MAC address is available. That way, you avoid multiple devices on one LAN using the same default MAC address.

Yes, I'll add a comment about the OUI and also use 'get_random_int() % 255' to generate the latest 3 octets (controller specific).

Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

drivers/net/ethernet/sunplus/sp_int.c line 34

Quoted 10 lines
drivers/net/ethernet/sunplus/sp_int.c new file +10 -0
+	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);
+		}

Looks very odd. The two netdev should be independent.

Quoted 8 lines
Quoted 4 lines

I don't understand your comment.
ndev checks PORT_ABILITY_LINK_ST_P0
ndev2 checks PORT_ABILITY_LINK_ST_P1
They are independent already.

I would try to remove the mac->next_ndev. I think without that, you will get a cleaner abstraction. You might want to keep an array of mac pointers in your top level shared structure.

Yes, I'll define a array (pointer to struct net_dev or mac) in driver private (shared) structure to access to all net devices. No more mac->next_ndev;.

Quoted 1 line

Andrew

Thank you very much for your review.

Best regards,
Wells

#53 lore raw

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

In reply to Andrew Lunn

Hi Andrew,

Regarding hardware 'auto rmii' function of SP7021, we find a way to 'disable' it.

We can use 'force' mode of MAC to set link up/down, speeds, full/half-duplex, flow-control forcibly.

Although MAC still keeps sending MDIO commands to PHY and get its status, but the read-back status has no use because MAC is in 'force' mode.

Due to hardware design, we still need to set PHY address, because MDIO controller of SP7021 only sends out MDIO commands with the same address listed in PHY address registers. The function below needs to be kept.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+		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); }

As i said before, the hardware never directly communicates with the PHY. So you can remove this.

If it is ok, I'll modify code and send a new patch.

Best regards,
Wells

Quoted 387 lines
Quoted 4 lines
Quoted 1 line

+void rx_descs_flush(struct sp_common *comm)

As both Florian and I have said, you need a prefix for all your functions, structures, etc. sp_ is not the best prefix either, it is not very unique.

spl2sw_ would be better.

I'll add prefix spl2sw for all functions, structures, file-names in next patch.

I thought comment for revising prefix is only for structures, function and file name with prefix l2sw_ because 'l2sw_' has been used by other modules.

Now I know prefix is necessary for all in this driver, except local variables and structure members.

Quoted 7 lines

drivers/net/ethernet/sunplus/sp_desc.c line 13

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +4 -0
+void rx_descs_clean(struct sp_common *comm) {
+	u32 i, j;
+	struct mac_desc *rx_desc;
+	struct skb_info *rx_skbinfo;

netdev wants reverse christmas tree. You need to change the order of your local variables, longest lines first, shorted last.

Yes, I'll rearrange local variables to 'reverse Christmas tree' order in next patch.

Quoted 61 lines
Quoted 61 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +60 -0
+
+	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;
+	}
+}
 
+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);
+			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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c line 163

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -0
+		}
+	}
+
+	return 0;
+
+MEM_ALLOC_FAIL:

lower case labels. Didn't somebody already say that?

I'll modify all labels to lowercase in next patch. Yes, Denis said that but patch not yet sent out.

Quoted 8 lines

drivers/net/ethernet/sunplus/sp_desc.c

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -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

Avoid #if statements. Why is this needed?

Yes, I'll remove the #if statement in next patch.
It is indeed not necessary.
RX_DESC_QUEUE_NUM is equal to 2.

Quoted 11 lines
Quoted 11 lines

+++ b/drivers/net/ethernet/sunplus/sp_driver.c

drivers/net/ethernet/sunplus/sp_driver.c new file +9 -0
@@ -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>
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_driver.c line 14

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_driver.c new file +5 -0
+#include "sp_driver.h"
+#include "sp_phy.h"
+
+static const char def_mac_addr[ETHERNET_MAC_ADDR_LEN] = {
+	0xfc, 0x4b, 0xbc, 0x00, 0x00, 0x00

This does not have the locally administered bit set. Should it? Or is this and address from your OUI?

This is default MAC address when MAC address in NVMEM is not found. Fc:4b:bc:00:00:00 is OUI of "Sunplus Technology Co., Ltd.". Can I keep this? or it should be removed?

Quoted 4 lines

Review line 16

Quoted 2 lines
(quoted diff) +2 -0
+static void ethernet_set_rx_mode(struct net_device *ndev) {
+	if (ndev) {

How can ndev be NULL?

Yes, I'll remove 'if (ndev) {' statement in next patch. It is redundant.

Quoted 7 lines
Quoted 7 lines
drivers/net/ethernet/sunplus/sp_desc.c new file +6 -0
+++ 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.
+ */
+
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 9

Quoted 4 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +4 -0
+#include <linux/iopoll.h>
+#include "sp_hal.h"
+
+void hal_mac_stop(struct sp_mac *mac)

I suggest you avoid any references to hal. It makes people think you have ported a driver from some other operating system and then put a layer of code on top of it. That is not how you do it in Linux. This is a Linux driver, nothing else.

Yes, I'll change file name 'sp_hal.c' to 'spl2sw_hw.c'. Function name in this file will also be changed, for example:
hal_mac_stop() --> spl2sw_hw_mac_stop()

Quoted 4 lines

Review line 11

Quoted 2 lines
(quoted diff) +2 -0
+void hal_mac_reset(struct sp_mac *mac) { }
+

Should not exist.

Yes, I'll remove it in next patch.

Quoted 8 lines
Quoted 8 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +8 -0
+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) +
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 62

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+	      (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);

Is this actually adding an entry into the address translation table?
If so, make this clear in the function name. You are not setting the MAC address, you

are

Quoted 1 line

just adding a static forwarding entry.

Yes, this is actually adding an entry into address table. I'll change function name to spl2sw_mac_add_addr() in next patch. Is the name ok?

Quoted 32 lines
Quoted 32 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +31 -0
+
+	// 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_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
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+		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));

This looks like it belongs in the ethtool code.

This function sets receiving mode.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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,
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c line 262

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +6 -0
+				comm->sp_reg_base + SP_PHY_CNTL_REG1);
+	if (ret == 0)
+		return val >> 16;
+	else
+		return ret;
+}

Should go with the other mdio code.

I'll move it into 'spl2sw_mdio.c' in next patch.

I put all hardware-related functions in sp_hal.c (will be changed to spl2sw_hw.c). All functions in other files won't touch hardware registers.
This seems not Linux driver style.

Quoted 9 lines
Quoted 9 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +9 -0
+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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_hal.c

Quoted 5 lines
drivers/net/ethernet/sunplus/sp_hal.c new file +5 -0
+		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); }

As i said before, the hardware never directly communicates with the PHY. So you can remove this.

I'll remove this function in next patch.

But now I cannot find a way to disable hardware 'auto rmii' function. If I remove this function right now, MAC may get wrong status of PHY from wrong address because SP7021 MAC communicates with PHY automatically. This may cause more problem.

I am consulting with ASIC engineer. Hopefully, someone can find a way to disable the auto function.

Quoted 9 lines

drivers/net/ethernet/sunplus/sp_int.c line 18

Quoted 7 lines
drivers/net/ethernet/sunplus/sp_int.c new file +7 -0
+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);

phylib should be handling the carrier for you.

I'll remove this function in next patch.
If 'auto rmii' function is removed, we no more need this function.

Quoted 4 lines
Quoted 4 lines
drivers/net/ethernet/sunplus/sp_int.c new file +4 -0
+	if (mac->next_ndev) {
+		struct net_device *ndev2 = mac->next_ndev;
+
+		if (!netif_carrier_ok(ndev2) && (reg & PORT_ABILITY_LINK_ST_P1)) {
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_int.c line 34

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_int.c new file +6 -0
+			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);
+		}

Looks very odd. The two netdev should be independent.

I don't understand your comment.
ndev checks PORT_ABILITY_LINK_ST_P0
ndev2 checks PORT_ABILITY_LINK_ST_P1
They are independent already.

Quoted 30 lines
Quoted 30 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +23 -0
new file mode 100644
index 0000000..f6a7e64
@@ -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;
Quoted 8 lines

drivers/net/ethernet/sunplus/sp_mdio.c line 32

Quoted 6 lines
drivers/net/ethernet/sunplus/sp_mdio.c new file +6 -0
+
+	return 0;
+}
+
+static int mii_read(struct mii_bus *bus, int phy_id, int regnum) {
+	struct sp_mac *mac = bus->priv;

What happened about my request to return -EOPNOTSUPP for C45 requests?

Sorry for overlooking the comment!
I am not sure how to check C45 request. Should I add statements like:

	if (regnum & MII_ADDR_C45)
		Return -EOPNOTSUPP;

for mdio_read() and mdio_write()?

Quoted 1 line

Andrew

Thank you very much for your review!

Best regards,
Wells

#54 lore raw

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

Quoted 4 lines

Due to hardware design, we still need to set PHY address, because MDIO controller of SP7021 only sends out MDIO commands with the same address listed in PHY address registers. The function below needs to be kept.

I suggest you actually set it to some other address. One of the good/bad things about MDIO is you have no idea if the device is there. A read to a device which does not exist just returns 0xffff, not an error. So i would set the address of 0x1f. I've never seen a PHY actually use that address.

Andrew

#55 lore raw

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

Hi Andrew,

I set phy-id registers to 30 and 31 and found the read-back values of mdio read commands from CPU are all 0x0000.

I consulted with an ASIC engineer. She confirmed that if phy-id of a mdio command from CPU does not match any phy-id registers, the mdio command will not be sent out.

She explained if phy-id of a mdio command does not match any phy-id registers (represent addresses of external PHYs), why MAC needs to send a command to non-existing PHY?

Best regards,

Wells Lu
呂芳騰

智能運算專案/Smart Computing Program
家庭平台事業群/Home Entertainment Business Unit
凌陽科技/Sunplus Technology Co., Ltd.
地址:300新竹科學園區創新一路19號
19, Innovation 1st Road,
Science-based Industrial Park
Hsin-Chu, Taiwan 300
TEL:886-3-5786005 ext. 2580

Quoted 21 lines

-----Original Message-----
From: Andrew Lunn <[email protected]>
Sent: Thursday, November 25, 2021 11:21 PM
To: Wells Lu 呂芳騰 <[email protected]>
Cc: Wells Lu <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Vincent Shih 施錕鴻 <[email protected]>
Subject: Re: [PATCH v2 2/2] net: ethernet: Add driver for Sunplus SP7021

Quoted 4 lines

Due to hardware design, we still need to set PHY address, because MDIO controller of SP7021 only sends out MDIO commands with the same address listed in PHY address registers. The function below needs to be kept.

I suggest you actually set it to some other address. One of the good/bad things about MDIO is you have no idea if the device is there. A read to a device which does not exist just returns 0xffff, not an error. So i would set the address of 0x1f. I've never seen a PHY actually use that address.

Andrew

#56 lore raw

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

Quoted 5 lines · Wells Lu 呂芳騰

Hi Andrew,

I set phy-id registers to 30 and 31 and found the read-back values of mdio read commands from CPU are all 0x0000.

Quoted 7 lines

I consulted with an ASIC engineer. She confirmed that if phy-id of a mdio command from CPU does not match any phy-id registers, the mdio command will not be sent out.

She explained if phy-id of a mdio command does not match any phy-id registers (represent addresses of external PHYs), why MAC needs to send a command to non-existing PHY?

Reads or writes on a real PHY which Linux is driving can have side effects. There is a link statue register which latches. Read it once, you get the last status, read it again, you get the current status. If the MAC hardware is reading this register as well a Linux, bad things will happen. A read on the interrupt status register often clears the interrupts. So Linux will not see the interrupts.

So you need to make sure you hardware is not touching a PHY which Linux uses. Which is why i suggested using MDIO bus address 31, which generally does not have a PHY at that address.

Andrew

#57 lore raw

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

Hi Andrew,

From data provided by ASIC engineer, MAC of SP7021 only reads the 4 registers of PHY:
0: Control register
1: Status register
4: Auto-negotiation advertisement register
5: Auto-negotiation link partner ability register

It does not read any other registers of PHY.

Best regards,
Wells Lu

Quoted 24 lines
Quoted 5 lines

Hi Andrew,

I set phy-id registers to 30 and 31 and found the read-back values of mdio read commands from CPU are all 0x0000.

Quoted 7 lines

I consulted with an ASIC engineer. She confirmed that if phy-id of a mdio command from CPU does not match any phy-id registers, the mdio command will not be sent out.

She explained if phy-id of a mdio command does not match any phy-id registers (represent addresses of external PHYs), why MAC needs to send a command to non-existing PHY?

Reads or writes on a real PHY which Linux is driving can have side effects. There is a link statue register which latches. Read it once, you get the last status, read it again, you get the current status. If the MAC hardware is reading this register as well a Linux, bad things will happen. A read on the interrupt status register often clears the interrupts. So Linux will not see the interrupts.

So you need to make sure you hardware is not touching a PHY which Linux uses. Which is why i suggested using MDIO bus address 31, which generally does not have a PHY at that address.

Andrew

#58 lore raw

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

Quoted 7 lines · Wells Lu 呂芳騰

Hi Andrew,

From data provided by ASIC engineer, MAC of SP7021 only reads the 4 registers of PHY:
0: Control register
1: Status register

This is the register which has latching of the link status. genphy_update_link() expects this latching behaviour, and if the hardware reads the register, that behaviour is not going to happen.

Andrew

#59 lore raw

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

Hi Andrew,

I read specification of ICPlus IP101G (10M/100M PHY). Bits of register 0 (control) and register 1 (status) are R/W or RO type. They will not be cleared after read. No matter how many times they are read, the read-back value is the same.

For example,
Value of register 0 (control) is 0x3100
Value of register 1 (status) is 0x786d

The read-back values are always the same unless you unplug the cable.

Besides, we use polling mode (phydev->irq = PHY_POLL) PHY state-machine is triggered by using 1-Hz work- queue, not interrupt.

We didn't find any problem after many tests after using 'force' mode.

Can we go with this approach?

Best regards,
Wells

Quoted 13 lines
Quoted 7 lines

Hi Andrew,

From data provided by ASIC engineer, MAC of SP7021 only reads the 4 registers of PHY:
0: Control register
1: Status register

This is the register which has latching of the link status. genphy_update_link() expects this latching behaviour, and if the hardware reads the register, that behaviour is not going to happen.

Andrew

#60 lore raw

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

Quoted 7 lines · Wells Lu 呂芳騰

Hi Andrew,

I read specification of ICPlus IP101G (10M/100M PHY). Bits of register 0 (control) and register 1 (status) are R/W or RO type. They will not be cleared after read. No matter how many times they are read, the read-back value is the same.

Please read 802.3,

Section 22.2.4.2: Status register (Register 1)

Table 22-8 Status register bit definitions

Bit 1.2 Link Status is marked as RO/LL, meaning read only Latching low.

Quoted 1 line

Can we go with this approach?

You need to not make any read on the PHY which Linux is driving. Configure the hardware to read on an address where there is no PHY.

Andrew

#61 lore raw

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

Hi Andrew,

Thanks a lot for explanation!.

Quoted 3 lines

You need to not make any read on the PHY which Linux is driving. Configure the hardware to read on an address where there is no PHY.

I will modify code to set hardware external PHY address to 31. In mii_read() or mii_write() functions, set hardware external PHY address to real PHY address, so that mii read or write command can be sent out by hardware. Set hardware external PHY address back to 31 after mii read or write command done.

Best regards,
Wells Lu