diff mbox series

[net-next,v9,6/6] net: stmmac: dwmac-loongson: Add GNET support

Message ID 3e9560ea34d507344d38a978a379f13bf6124d1b.1712407009.git.siyanteng@loongson.cn (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series stmmac: Add Loongson platform support | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 29 this patch: 958
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 10 maintainers not CCed: mcoquelin.stm32@gmail.com richardcochran@gmail.com bartosz.golaszewski@linaro.org ahalaney@redhat.com linux-stm32@st-md-mailman.stormreply.com linux-arm-kernel@lists.infradead.org horms@kernel.org kuba@kernel.org edumazet@google.com pabeni@redhat.com
netdev/build_clang fail Errors and warnings before: 22 this patch: 958
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 30 this patch: 970
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 579 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Yanteng Si April 6, 2024, 1:24 p.m. UTC
There are two types of Loongson DWGMAC. The first type shares the same
register definitions and has similar logic as dwmac1000. The second type
uses several different register definitions, we think it is necessary to
distinguish rx and tx, so we split these bits into two.

Simply put, we split some single bit fields into double bits fileds:

     Name              Tx          Rx

DMA_INTR_ENA_NIE = 0x00040000 | 0x00020000;
DMA_INTR_ENA_AIE = 0x00010000 | 0x00008000;
DMA_STATUS_NIS   = 0x00040000 | 0x00020000;
DMA_STATUS_AIS   = 0x00010000 | 0x00008000;
DMA_STATUS_FBI   = 0x00002000 | 0x00001000;

Therefore, when using, TX and RX must be set at the same time.

How to use them:
 1. Create the Loongson GNET-specific
 stmmac_dma_ops.dma_interrupt()
 stmmac_dma_ops.init_chan()
 methods in the dwmac-loongson.c driver. Adding all the
 Loongson-specific macros

 2. Create a Loongson GNET-specific platform setup method with the next
 semantics:
    + allocate stmmac_dma_ops instance and initialize it with
      dwmac1000_dma_ops.
    + override the stmmac_dma_ops.{dma_interrupt, init_chan} with
      the pointers to the methods defined in 2.
    + allocate mac_device_info instance and initialize the
      mac_device_info.dma field with a pointer to the new
      stmmac_dma_ops instance.
    + initialize mac_device_info in a way it's done in
      dwmac1000_setup().

 3. Initialize plat_stmmacenet_data.setup() with the pointer to the
 method created in 2.

GNET features:

 Speeds: 10/100/1000Mbps
 DMA-descriptors type: normal and enhanced
 L3/L4 filters availability: support
 VLAN hash table filter: support
 PHY-interface: GMII
 Remote Wake-up support: support
 Mac Management Counters (MMC): support
 DMA chennel number: 0x10 device is 8 and 0x37 device is 1

Others:
GNET integrates both MAC and PHY chips inside.
gnet device: LS7A2000, LS2K2000, the chip connection between the mac and
             phy of these devices is not normal and requires two rounds of
             negotiation; LS7A2000 does not support half-duplex and
	     multi-channel;

	     To enable multi-channel on LS2K2000, you need to turn off
	     hardware checksum.

**Note**: Only the LS2K2000's IP core is 0x10, while the IP cores of other
devices are 0x37.

Signed-off-by: Yanteng Si <siyanteng@loongson.cn>
Signed-off-by: Feiyang Chen <chenfeiyang@loongson.cn>
Signed-off-by: Yinggang Gu <guyinggang@loongson.cn>
---
 .../ethernet/stmicro/stmmac/dwmac-loongson.c  | 502 ++++++++++++++++--
 .../ethernet/stmicro/stmmac/dwmac1000_dma.c   |   1 +
 include/linux/stmmac.h                        |   1 +
 3 files changed, 462 insertions(+), 42 deletions(-)

Comments

Andrew Lunn April 6, 2024, 2:46 p.m. UTC | #1
> +static void loongson_gnet_fix_speed(void *priv, unsigned int speed,
> +				    unsigned int mode)
> +{
> +	struct loongson_data *ld = (struct loongson_data *)priv;
> +	struct net_device *ndev = dev_get_drvdata(ld->dev);
> +	struct stmmac_priv *ptr = netdev_priv(ndev);
> +
> +	/* The controller and PHY don't work well together.
> +	 * We need to use the PS bit to check if the controller's status
> +	 * is correct and reset PHY if necessary.
> +	 * MAC_CTRL_REG.15 is defined by the GMAC_CONTROL_PS macro.
> +	 */
> +	if (speed == SPEED_1000) {
> +		if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15))

It would be good to add a #define using BIT(15) for this PS bit. Also,
what does PS actually mean?

> +	priv->synopsys_id = 0x37;

hwif.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50) {
hwif.c:	priv->synopsys_id = id;
hwif.c:		/* Use synopsys_id var because some setups can override this */
hwif.c:		if (priv->synopsys_id < entry->min_id)
stmmac_ethtool.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50)
stmmac.h:	int synopsys_id;
stmmac_main.c:			if (priv->synopsys_id < DWMAC_CORE_4_10)
stmmac_main.c:	if (priv->synopsys_id >= DWMAC_CORE_4_00 ||
stmmac_main.c:		if (priv->synopsys_id < DWMAC_CORE_4_00)
stmmac_main.c:	if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
stmmac_main.c:	if (priv->synopsys_id < DWMAC_CORE_5_20)
stmmac_main.c:	else if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
stmmac_mdio.c:		if (priv->synopsys_id < DWXGMAC_CORE_2_20) {

Please add a #define for this 0x37.

Who allocated this value? Synopsys?

/* Synopsys Core versions */
#define DWMAC_CORE_3_40         0x34
#define DWMAC_CORE_3_50         0x35
#define DWMAC_CORE_4_00         0x40
#define DWMAC_CORE_4_10         0x41

0x37 makes it somewhere between a 3.5 and 4.0.

stmmac_ethtool.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50)
stmmac_main.c:	if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||

Does the hardware actually provide what these two bits of code
require? Have you reviewed all similar bits of code to confirm they
are correct for your hardware?

	Andrew
Yanteng Si April 7, 2024, 9:06 a.m. UTC | #2
在 2024/4/6 22:46, Andrew Lunn 写道:
>> +	 */
>> +	if (speed == SPEED_1000) {
>> +		if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15))
> It would be good to add a #define using BIT(15) for this PS bit. Also,
OK.
> what does PS actually mean?

In DW GMAC v3.73a:

It is the bit 15 of MAC Configuration Register


Port Select

This bit selects the Ethernet line speed.

* 0: For 1000 Mbps operations

* 1: For 10 or 100 Mbps operations


In 10 or 100 Mbps operations, this bit, along with FES bit, selects the 
exact line
speed. In the 10/100 Mbps-only (always 1) or 1000 Mbps-only (always 0)
configurations, this bit is read-only with the appropriate value. In default
10/100/1000 Mbps configuration, this bit is R_W. The mac_portselect_o or
mac_speed_o[1] signal reflects the value of this bit.
>
>> +	priv->synopsys_id = 0x37;
> hwif.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50) {
> hwif.c:	priv->synopsys_id = id;
> hwif.c:		/* Use synopsys_id var because some setups can override this */
> hwif.c:		if (priv->synopsys_id < entry->min_id)
> stmmac_ethtool.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50)
> stmmac.h:	int synopsys_id;
> stmmac_main.c:			if (priv->synopsys_id < DWMAC_CORE_4_10)
> stmmac_main.c:	if (priv->synopsys_id >= DWMAC_CORE_4_00 ||
> stmmac_main.c:		if (priv->synopsys_id < DWMAC_CORE_4_00)
> stmmac_main.c:	if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
> stmmac_main.c:	if (priv->synopsys_id < DWMAC_CORE_5_20)
> stmmac_main.c:	else if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
> stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
> stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
> stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
> stmmac_mdio.c:		if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
>
> Please add a #define for this 0x37.
>
> Who allocated this value? Synopsys?
It look like this.
>
> /* Synopsys Core versions */
> #define DWMAC_CORE_3_40         0x34
> #define DWMAC_CORE_3_50         0x35
> #define DWMAC_CORE_4_00         0x40
> #define DWMAC_CORE_4_10         0x41
>
> 0x37 makes it somewhere between a 3.5 and 4.0.

Yeah,

How about defining it in 
drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c?

>
> stmmac_ethtool.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50)
> stmmac_main.c:	if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
>
> Does the hardware actually provide what these two bits of code
> require? Have you reviewed all similar bits of code to confirm they
> are correct for your hardware?

Yes, because in fact the IP core of our gnet is 0x37, but some chips 
(2k2000) split

the rx/tx register definition from one into two. In order to distinguish 
them, 0x10

was created. it is not worth adding a new entry for this.


According to Serge's comment, we made these devices work by overwriting
priv->synopsys_id = 0x37 and mac->dma = <LS_dma_ops>.


Thanks,

Yanteng
Andrew Lunn April 7, 2024, 3:27 p.m. UTC | #3
On Sun, Apr 07, 2024 at 05:06:34PM +0800, Yanteng Si wrote:
> 
> 在 2024/4/6 22:46, Andrew Lunn 写道:
> > > +	 */
> > > +	if (speed == SPEED_1000) {
> > > +		if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15))
> > It would be good to add a #define using BIT(15) for this PS bit. Also,
> OK.
> > what does PS actually mean?
> 
> In DW GMAC v3.73a:
> 
> It is the bit 15 of MAC Configuration Register
> 
> 
> Port Select

Since this is a standard bit in a register, please add a #define for
it. Something like

#define MAC_CTRL_PORT_SELECT_10_100 BIT(15)

maybe in commom.h? I don't know this driver too well, so it might have
a different naming convention. 

	if (speed == SPEED_1000) {
		if (readl(ptr->ioaddr + MAC_CTRL_REG) & MAC_CTRL_PORT_SELECT_10_100)
		/* Word around hardware bug, restart autoneg */

is more obvious what is going on.

> > > +	priv->synopsys_id = 0x37;
> > hwif.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50) {
> > hwif.c:	priv->synopsys_id = id;
> > hwif.c:		/* Use synopsys_id var because some setups can override this */
> > hwif.c:		if (priv->synopsys_id < entry->min_id)
> > stmmac_ethtool.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50)
> > stmmac.h:	int synopsys_id;
> > stmmac_main.c:			if (priv->synopsys_id < DWMAC_CORE_4_10)
> > stmmac_main.c:	if (priv->synopsys_id >= DWMAC_CORE_4_00 ||
> > stmmac_main.c:		if (priv->synopsys_id < DWMAC_CORE_4_00)
> > stmmac_main.c:	if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
> > stmmac_main.c:	if (priv->synopsys_id < DWMAC_CORE_5_20)
> > stmmac_main.c:	else if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
> > stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
> > stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
> > stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
> > stmmac_mdio.c:		if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
> > 
> > Please add a #define for this 0x37.
> > 
> > Who allocated this value? Synopsys?
> It look like this.

That did not answer my question. Did Synopsys allocate this value?  If
not, what happens when Synopsys does produce a version which makes use
of this value?

> > 
> > /* Synopsys Core versions */
> > #define DWMAC_CORE_3_40         0x34
> > #define DWMAC_CORE_3_50         0x35
> > #define DWMAC_CORE_4_00         0x40
> > #define DWMAC_CORE_4_10         0x41
> > 
> > 0x37 makes it somewhere between a 3.5 and 4.0.
> 
> Yeah,
> 
> How about defining it in
> drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c?

No, because of the version comparisons within the code, developers
need to know what versions actually exist. So you should include it
along side all the other values.

      Andrew
Yanteng Si April 8, 2024, 2:30 a.m. UTC | #4
在 2024/4/7 23:27, Andrew Lunn 写道:
> On Sun, Apr 07, 2024 at 05:06:34PM +0800, Yanteng Si wrote:
>> 在 2024/4/6 22:46, Andrew Lunn 写道:
>>>> +	 */
>>>> +	if (speed == SPEED_1000) {
>>>> +		if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15))
>>> It would be good to add a #define using BIT(15) for this PS bit. Also,
>> OK.
>>> what does PS actually mean?
>> In DW GMAC v3.73a:
>>
>> It is the bit 15 of MAC Configuration Register
>>
>>
>> Port Select
> Since this is a standard bit in a register, please add a #define for
> it. Something like
>
> #define MAC_CTRL_PORT_SELECT_10_100 BIT(15)
>
> maybe in commom.h? I don't know this driver too well, so it might have
OK.
> a different naming convention.
>
> 	if (speed == SPEED_1000) {
> 		if (readl(ptr->ioaddr + MAC_CTRL_REG) & MAC_CTRL_PORT_SELECT_10_100)
> 		/* Word around hardware bug, restart autoneg */
>
> is more obvious what is going on.
great!
>
>>>> +	priv->synopsys_id = 0x37;
>>> hwif.c:		if (priv->synopsys_id >= DWMAC_CORE_3_50) {
>>> stmmac_mdio.c:	if (priv->synopsys_id < DWXGMAC_CORE_2_20 &&
>>> stmmac_mdio.c:		if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
>>>
>>> Please add a #define for this 0x37.
>>>
>>> Who allocated this value? Synopsys?
>> It look like this.
> That did not answer my question. Did Synopsys allocate this value?  If
> not, what happens when Synopsys does produce a version which makes use
> of this value?

Hmm, that will not happen, because the value already exists, and we can 
find it in the code.

title: Synopsys DesignWare MAC

maintainers:
   - Alexandre Torgue <alexandre.torgue@foss.st.com>
   - Giuseppe Cavallaro <peppe.cavallaro@st.com>
   - Jose Abreu <joabreu@synopsys.com>

# Select every compatible, including the deprecated ones. This way, we
# will be able to report a warning when we have that compatible, since
# we will validate the node thanks to the select, but won't report it
# as a valid value in the compatible property description
select:
   properties:
     compatible:
       contains:
         enum:
           - snps,dwmac
           - snps,dwmac-3.40a
           - snps,dwmac-3.50a
           - snps,dwmac-3.610
           - snps,dwmac-3.70a
           - snps,dwmac-3.710
           - snps,dwmac-4.00
           - snps,dwmac-4.10a
           - snps,dwmac-4.20a
           - snps,dwmac-5.10a
           - snps,dwmac-5.20
           - snps,dwxgmac
           - snps,dwxgmac-2.10

           # Deprecated
           - st,spear600-gmac

There are a lot of calls like that. Let's grep -rn dwmac-3.70a:

drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c:57: { .compatible = 
"snps,dwmac-3.70a"},
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c:524: 
of_device_is_compatible(np, "snps,dwmac-3.70a") ||

arch/arm64/boot/dts/amlogic/meson-axg.dtsi:279: "snps,dwmac-3.70a",
arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi:171: "snps,dwmac-3.70a",
arch/arm64/boot/dts/amlogic/meson-gx.dtsi:585: "snps,dwmac-3.70a",
arch/arm64/boot/dts/amlogic/meson-s4.dtsi:489: "snps,dwmac-3.70a",
arch/loongarch/boot/dts/.loongson-2k0500-ref.dtb.dts.tmp:114: compatible 
= "snps,dwmac-3.70a";

......

>>> /* Synopsys Core versions */
>>> #define DWMAC_CORE_3_40         0x34
>>> #define DWMAC_CORE_3_50         0x35
>>> #define DWMAC_CORE_4_00         0x40
>>> #define DWMAC_CORE_4_10         0x41
>>>
>>> 0x37 makes it somewhere between a 3.5 and 4.0.
>> Yeah,
>>
>> How about defining it in
>> drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c?
> No, because of the version comparisons within the code, developers
> need to know what versions actually exist. So you should include it
> along side all the other values.

OK, Let's:

@@ -29,6 +29,7 @@
  /* Synopsys Core versions */
  #define        DWMAC_CORE_3_40         0x34
  #define        DWMAC_CORE_3_50         0x35
+#define        DWMAC_CORE_3_70         0x37
  #define        DWMAC_CORE_4_00         0x40
  #define DWMAC_CORE_4_10                0x41
  #define DWMAC_CORE_5_00                0x50


Thanks,

Yanteng
diff mbox series

Patch

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
index 84ead5edd6d0..26ce55d520a8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
@@ -8,8 +8,70 @@ 
 #include <linux/device.h>
 #include <linux/of_irq.h>
 #include "stmmac.h"
+#include "dwmac_dma.h"
+#include "dwmac1000.h"
+
+/* Normal Loongson Tx Summary */
+#define DMA_INTR_ENA_NIE_TX_LOONGSON	0x00040000
+/* Normal Loongson Rx Summary */
+#define DMA_INTR_ENA_NIE_RX_LOONGSON	0x00020000
+#define DMA_INTR_NORMAL_LOONGSON	(DMA_INTR_ENA_NIE_TX_LOONGSON | \
+					 DMA_INTR_ENA_NIE_RX_LOONGSON | \
+					 DMA_INTR_ENA_RIE | DMA_INTR_ENA_TIE)
+
+/* Abnormal Loongson Tx Summary */
+#define DMA_INTR_ENA_AIE_TX_LOONGSON	0x00010000
+/* Abnormal Loongson Rx Summary */
+#define DMA_INTR_ENA_AIE_RX_LOONGSON	0x00008000
+
+#define DMA_INTR_ABNORMAL_LOONGSON	(DMA_INTR_ENA_AIE_TX_LOONGSON | \
+					 DMA_INTR_ENA_AIE_RX_LOONGSON | \
+					 DMA_INTR_ENA_FBE | DMA_INTR_ENA_UNE)
+
+#define DMA_INTR_DEFAULT_MASK_LOONGSON	(DMA_INTR_NORMAL_LOONGSON | \
+					 DMA_INTR_ABNORMAL_LOONGSON)
+
+/* Normal Loongson Tx Interrupt Summary */
+#define DMA_STATUS_NIS_TX_LOONGSON	0x00040000
+/* Normal Loongson Rx Interrupt Summary */
+#define DMA_STATUS_NIS_RX_LOONGSON	0x00020000
+
+/* Abnormal Loongson Tx Interrupt Summary */
+#define DMA_STATUS_AIS_TX_LOONGSON	0x00010000
+/* Abnormal Loongson Rx Interrupt Summary */
+#define DMA_STATUS_AIS_RX_LOONGSON	0x00008000
+
+/* Fatal Loongson Tx Bus Error Interrupt */
+#define DMA_STATUS_FBI_TX_LOONGSON	0x00002000
+/* Fatal Loongson Rx Bus Error Interrupt */
+#define DMA_STATUS_FBI_RX_LOONGSON	0x00001000
+
+#define DMA_STATUS_MSK_COMMON_LOONGSON	(DMA_STATUS_NIS_TX_LOONGSON | \
+					 DMA_STATUS_NIS_RX_LOONGSON | \
+					 DMA_STATUS_AIS_TX_LOONGSON | \
+					 DMA_STATUS_AIS_RX_LOONGSON | \
+					 DMA_STATUS_FBI_TX_LOONGSON | \
+					 DMA_STATUS_FBI_RX_LOONGSON)
+
+#define DMA_STATUS_MSK_RX_LOONGSON	(DMA_STATUS_ERI | DMA_STATUS_RWT | \
+					 DMA_STATUS_RPS | DMA_STATUS_RU  | \
+					 DMA_STATUS_RI  | DMA_STATUS_OVF | \
+					 DMA_STATUS_MSK_COMMON_LOONGSON)
+
+#define DMA_STATUS_MSK_TX_LOONGSON	(DMA_STATUS_ETI | DMA_STATUS_UNF | \
+					 DMA_STATUS_TJT | DMA_STATUS_TU  | \
+					 DMA_STATUS_TPS | DMA_STATUS_TI  | \
+					 DMA_STATUS_MSK_COMMON_LOONGSON)
 
 #define PCI_DEVICE_ID_LOONGSON_GMAC	0x7a03
+#define PCI_DEVICE_ID_LOONGSON_GNET	0x7a13
+#define LOONGSON_DWMAC_CORE_1_00	0x10	/* Loongson custom ip */
+#define LOONGSON_DWMAC_CORE_3_70	0x37
+#define CHANNEL_NUM	8	/*gnet of 2k2000 has 8 channel*/
+
+struct loongson_data {
+	struct device *dev;
+};
 
 struct stmmac_pci_info {
 	int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
@@ -43,6 +105,362 @@  static void loongson_default_data(struct pci_dev *pdev,
 	plat->rx_queues_cfg[0].pkt_route = 0x0;
 }
 
+static int loongson_dwmac_config_legacy(struct pci_dev *pdev,
+					struct plat_stmmacenet_data *plat,
+					struct stmmac_resources *res,
+					struct device_node *np)
+{
+	if (np) {
+		res->irq = of_irq_get_byname(np, "macirq");
+		if (res->irq < 0) {
+			dev_err(&pdev->dev, "IRQ macirq not found\n");
+			return -ENODEV;
+		}
+
+		res->wol_irq = of_irq_get_byname(np, "eth_wake_irq");
+		if (res->wol_irq < 0) {
+			dev_info(&pdev->dev,
+				 "IRQ eth_wake_irq not found, using macirq\n");
+			res->wol_irq = res->irq;
+		}
+
+		res->lpi_irq = of_irq_get_byname(np, "eth_lpi");
+		if (res->lpi_irq < 0) {
+			dev_err(&pdev->dev, "IRQ eth_lpi not found\n");
+			return -ENODEV;
+		}
+	} else {
+		res->irq = pdev->irq;
+		res->wol_irq = res->irq;
+	}
+
+	return 0;
+}
+
+static void loongson_gnet_dma_init_channel(struct stmmac_priv *priv,
+					   void __iomem *ioaddr,
+					   struct stmmac_dma_cfg *dma_cfg,
+					   u32 chan)
+{
+	int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
+	int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl;
+	u32 value;
+
+	/* common channel control register config */
+	value = readl(ioaddr + DMA_CHAN_BUS_MODE(chan));
+
+	/* Set the DMA PBL (Programmable Burst Length) mode.
+	 *
+	 * Note: before stmmac core 3.50 this mode bit was 4xPBL, and
+	 * post 3.5 mode bit acts as 8*PBL.
+	 */
+	if (dma_cfg->pblx8)
+		value |= DMA_BUS_MODE_MAXPBL;
+	value |= DMA_BUS_MODE_USP;
+	value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK);
+	value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT);
+	value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT);
+
+	/* Set the Fixed burst mode */
+	if (dma_cfg->fixed_burst)
+		value |= DMA_BUS_MODE_FB;
+
+	/* Mixed Burst has no effect when fb is set */
+	if (dma_cfg->mixed_burst)
+		value |= DMA_BUS_MODE_MB;
+
+	if (dma_cfg->atds)
+		value |= DMA_BUS_MODE_ATDS;
+
+	if (dma_cfg->aal)
+		value |= DMA_BUS_MODE_AAL;
+
+	writel(value, ioaddr + DMA_CHAN_BUS_MODE(chan));
+
+	/* Mask interrupts by writing to CSR7 */
+	writel(DMA_INTR_DEFAULT_MASK_LOONGSON, ioaddr +
+	       DMA_CHAN_INTR_ENA(chan));
+}
+
+static int loongson_gnet_dma_interrupt(struct stmmac_priv *priv,
+				       void __iomem *ioaddr,
+				       struct stmmac_extra_stats *x,
+				       u32 chan, u32 dir)
+{
+	struct stmmac_pcpu_stats *stats = this_cpu_ptr(priv->xstats.pcpu_stats);
+	u32 abnor_intr_status;
+	u32 nor_intr_status;
+	u32 fb_intr_status;
+	u32 intr_status;
+	int ret = 0;
+
+	/* read the status register (CSR5) */
+	intr_status = readl(ioaddr + DMA_CHAN_STATUS(chan));
+
+	if (dir == DMA_DIR_RX)
+		intr_status &= DMA_STATUS_MSK_RX_LOONGSON;
+	else if (dir == DMA_DIR_TX)
+		intr_status &= DMA_STATUS_MSK_TX_LOONGSON;
+
+	nor_intr_status = intr_status & (DMA_STATUS_NIS_TX_LOONGSON |
+		DMA_STATUS_NIS_RX_LOONGSON);
+	abnor_intr_status = intr_status & (DMA_STATUS_AIS_TX_LOONGSON |
+		DMA_STATUS_AIS_RX_LOONGSON);
+	fb_intr_status = intr_status & (DMA_STATUS_FBI_TX_LOONGSON |
+		DMA_STATUS_FBI_RX_LOONGSON);
+
+	/* ABNORMAL interrupts */
+	if (unlikely(abnor_intr_status)) {
+		if (unlikely(intr_status & DMA_STATUS_UNF)) {
+			ret = tx_hard_error_bump_tc;
+			x->tx_undeflow_irq++;
+		}
+		if (unlikely(intr_status & DMA_STATUS_TJT))
+			x->tx_jabber_irq++;
+
+		if (unlikely(intr_status & DMA_STATUS_OVF))
+			x->rx_overflow_irq++;
+
+		if (unlikely(intr_status & DMA_STATUS_RU))
+			x->rx_buf_unav_irq++;
+		if (unlikely(intr_status & DMA_STATUS_RPS))
+			x->rx_process_stopped_irq++;
+		if (unlikely(intr_status & DMA_STATUS_RWT))
+			x->rx_watchdog_irq++;
+		if (unlikely(intr_status & DMA_STATUS_ETI))
+			x->tx_early_irq++;
+		if (unlikely(intr_status & DMA_STATUS_TPS)) {
+			x->tx_process_stopped_irq++;
+			ret = tx_hard_error;
+		}
+		if (unlikely(intr_status & fb_intr_status)) {
+			x->fatal_bus_error_irq++;
+			ret = tx_hard_error;
+		}
+	}
+	/* TX/RX NORMAL interrupts */
+	if (likely(nor_intr_status)) {
+		if (likely(intr_status & DMA_STATUS_RI)) {
+			u32 value = readl(ioaddr + DMA_INTR_ENA);
+			/* to schedule NAPI on real RIE event. */
+			if (likely(value & DMA_INTR_ENA_RIE)) {
+				u64_stats_update_begin(&stats->syncp);
+				u64_stats_inc(&stats->rx_normal_irq_n[chan]);
+				u64_stats_update_end(&stats->syncp);
+				ret |= handle_rx;
+			}
+		}
+		if (likely(intr_status & DMA_STATUS_TI)) {
+			u64_stats_update_begin(&stats->syncp);
+			u64_stats_inc(&stats->tx_normal_irq_n[chan]);
+			u64_stats_update_end(&stats->syncp);
+			ret |= handle_tx;
+		}
+		if (unlikely(intr_status & DMA_STATUS_ERI))
+			x->rx_early_irq++;
+	}
+	/* Optional hardware blocks, interrupts should be disabled */
+	if (unlikely(intr_status &
+		     (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI)))
+		pr_warn("%s: unexpected status %08x\n", __func__, intr_status);
+
+	/* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */
+	writel((intr_status & 0x7ffff), ioaddr + DMA_CHAN_STATUS(chan));
+
+	return ret;
+}
+
+static void loongson_phylink_get_caps(struct stmmac_priv *priv)
+{
+	struct pci_dev *pdev = to_pci_dev(priv->device);
+	struct stmmac_resources res;
+	u32 loongson_gmac;
+
+	memset(&res, 0, sizeof(res));
+	res.addr = pcim_iomap_table(pdev)[0];
+	loongson_gmac = readl(res.addr + GMAC_VERSION) & 0xff;
+
+	/* The GMAC device with PCI ID 7a03 does not support any pause mode.
+	 * The GNET device (only 7A2000) does not support half-duplex.
+	 */
+	if (pdev->vendor == 0x7a03) {
+		priv->phylink_config.mac_capabilities = MAC_10FD | MAC_100FD |
+			MAC_1000FD;
+	} else {
+		priv->phylink_config.mac_capabilities = (MAC_ASYM_PAUSE |
+			MAC_SYM_PAUSE | MAC_10FD | MAC_100FD | MAC_1000FD);
+
+		if (loongson_gmac == LOONGSON_DWMAC_CORE_3_70) {
+			priv->phylink_config.mac_capabilities &= ~(MAC_10HD |
+				MAC_100HD | MAC_1000HD);
+		};
+	};
+}
+
+static void loongson_gnet_fix_speed(void *priv, unsigned int speed,
+				    unsigned int mode)
+{
+	struct loongson_data *ld = (struct loongson_data *)priv;
+	struct net_device *ndev = dev_get_drvdata(ld->dev);
+	struct stmmac_priv *ptr = netdev_priv(ndev);
+
+	/* The controller and PHY don't work well together.
+	 * We need to use the PS bit to check if the controller's status
+	 * is correct and reset PHY if necessary.
+	 * MAC_CTRL_REG.15 is defined by the GMAC_CONTROL_PS macro.
+	 */
+	if (speed == SPEED_1000) {
+		if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15))
+			phy_restart_aneg(ndev->phydev);
+	}
+}
+
+static struct mac_device_info *loongson_gnet_setup(void *apriv)
+{
+	struct stmmac_ops *loongson_dwmac_ops;
+	struct stmmac_priv *priv = apriv;
+	struct mac_device_info *mac;
+	struct stmmac_dma_ops *dma;
+
+	mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
+	if (!mac)
+		return NULL;
+
+	dma = devm_kzalloc(priv->device, sizeof(*dma), GFP_KERNEL);
+	if (!dma)
+		return NULL;
+
+	loongson_dwmac_ops = devm_kzalloc(priv->device,
+					  sizeof(*loongson_dwmac_ops),
+					  GFP_KERNEL);
+	if (!loongson_dwmac_ops)
+		return NULL;
+
+	/* The original IP-core version is 0x37 in all Loongson GNET
+	 * (2k2000 and 7a2000), but the GNET HW designers have changed the
+	 * GMAC_VERSION.SNPSVER field to the custom 0x10 value on the Loongson
+	 * 2k2000 MAC to emphasize the differences: multiple DMA-channels, AV
+	 * feature and GMAC_INT_STATUS CSR flags layout. Get back the
+	 * original value so the correct HW-interface would be
+	 * selected.
+	 */
+	priv->synopsys_id = 0x37;
+	priv->dev->priv_flags |= IFF_UNICAST_FLT;
+
+	*dma = dwmac1000_dma_ops;
+	dma->init_chan = loongson_gnet_dma_init_channel;
+	dma->dma_interrupt = loongson_gnet_dma_interrupt;
+	mac->dma = dma;
+
+	*loongson_dwmac_ops = dwmac1000_ops;
+	loongson_dwmac_ops->phylink_get_caps = loongson_phylink_get_caps;
+	mac->mac = loongson_dwmac_ops;
+
+	/* Pre-initialize the respective "mac" fields as it's done in
+	 * dwmac1000_setup()
+	 */
+	mac->pcsr = priv->ioaddr;
+	mac->multicast_filter_bins = priv->plat->multicast_filter_bins;
+	mac->unicast_filter_entries = priv->plat->unicast_filter_entries;
+	mac->mcast_bits_log2 = 0;
+
+	if (mac->multicast_filter_bins)
+		mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins);
+
+	mac->link.duplex = GMAC_CONTROL_DM;
+	mac->link.speed10 = GMAC_CONTROL_PS;
+	mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
+	mac->link.speed1000 = 0;
+	mac->link.speed_mask = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
+	mac->mii.addr = GMAC_MII_ADDR;
+	mac->mii.data = GMAC_MII_DATA;
+	mac->mii.addr_shift = 11;
+	mac->mii.addr_mask = 0x0000F800;
+	mac->mii.reg_shift = 6;
+	mac->mii.reg_mask = 0x000007C0;
+	mac->mii.clk_csr_shift = 2;
+	mac->mii.clk_csr_mask = GENMASK(5, 2);
+
+	return mac;
+}
+
+static int loongson_gnet_config_multi_msi(struct pci_dev *pdev,
+					  struct plat_stmmacenet_data *plat,
+					  struct stmmac_resources *res,
+					  struct device_node *np)
+{
+	int i, ret, vecs;
+
+	vecs = roundup_pow_of_two(CHANNEL_NUM * 2 + 1);
+	ret = pci_alloc_irq_vectors(pdev, vecs, vecs, PCI_IRQ_MSI);
+	if (ret < 0) {
+		dev_info(&pdev->dev,
+			 "MSI enable failed, Fallback to legacy interrupt\n");
+		return loongson_dwmac_config_legacy(pdev, plat, res, np);
+	}
+
+	res->irq = pci_irq_vector(pdev, 0);
+	res->wol_irq = 0;
+
+	/* INT NAME | MAC | CH7 rx | CH7 tx | ... | CH0 rx | CH0 tx |
+	 * --------- ----- -------- --------  ...  -------- --------
+	 * IRQ NUM  |  0  |   1    |   2    | ... |   15   |   16   |
+	 */
+	for (i = 0; i < CHANNEL_NUM; i++) {
+		res->rx_irq[CHANNEL_NUM - 1 - i] =
+			pci_irq_vector(pdev, 1 + i * 2);
+		res->tx_irq[CHANNEL_NUM - 1 - i] =
+			pci_irq_vector(pdev, 2 + i * 2);
+	}
+
+	plat->flags |= STMMAC_FLAG_MULTI_MSI_EN;
+
+	return 0;
+}
+
+static int loongson_gnet_data(struct pci_dev *pdev,
+			      struct plat_stmmacenet_data *plat)
+{
+	struct stmmac_resources res;
+	u32 loongson_gmac;
+
+	loongson_default_data(pdev, plat);
+
+	plat->multicast_filter_bins = 256;
+
+	plat->mdio_bus_data->phy_mask =  ~(u32)BIT(2);
+
+	plat->phy_addr = -1;
+	plat->phy_interface = PHY_INTERFACE_MODE_GMII;
+
+	plat->fix_mac_speed = loongson_gnet_fix_speed;
+
+	plat->dma_cfg->pbl = 32;
+	plat->dma_cfg->pblx8 = true;
+
+	plat->clk_ref_rate = 125000000;
+	plat->clk_ptp_rate = 125000000;
+
+	memset(&res, 0, sizeof(res));
+	res.addr = pcim_iomap_table(pdev)[0];
+
+	loongson_gmac = readl(res.addr + GMAC_VERSION) & 0xff;
+
+	if (loongson_gmac == LOONGSON_DWMAC_CORE_1_00) {
+		plat->rx_queues_to_use = CHANNEL_NUM;
+		plat->tx_queues_to_use = CHANNEL_NUM;
+	} else {
+		plat->tx_queues_to_use = 1;
+		plat->rx_queues_to_use = 1;
+	}
+
+	return 0;
+}
+
+static struct stmmac_pci_info loongson_gnet_pci_info = {
+	.setup = loongson_gnet_data,
+};
+
 static int loongson_gmac_data(struct pci_dev *pdev,
 			      struct plat_stmmacenet_data *plat)
 {
@@ -69,13 +487,16 @@  static struct stmmac_pci_info loongson_gmac_pci_info = {
 	.setup = loongson_gmac_data,
 };
 
-static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int loongson_dwmac_probe(struct pci_dev *pdev,
+				const struct pci_device_id *id)
 {
 	struct plat_stmmacenet_data *plat;
 	int ret, i, bus_id, phy_mode;
 	struct stmmac_pci_info *info;
 	struct stmmac_resources res;
+	struct loongson_data *ld;
 	struct device_node *np;
+	u32 loongson_gmac;
 
 	plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
 	if (!plat)
@@ -87,11 +508,16 @@  static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
 	if (!plat->mdio_bus_data)
 		return -ENOMEM;
 
-	plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL);
-	if (!plat->dma_cfg) {
-		ret = -ENOMEM;
-		goto err_put_node;
-	}
+	plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg),
+				     GFP_KERNEL);
+	if (!plat->dma_cfg)
+		return -ENOMEM;
+
+	ld = devm_kzalloc(&pdev->dev, sizeof(*ld), GFP_KERNEL);
+	if (!ld)
+		return -ENOMEM;
+
+	np = dev_of_node(&pdev->dev);
 
 	/* Enable pci device */
 	ret = pci_enable_device(pdev);
@@ -110,15 +536,6 @@  static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
 		break;
 	}
 
-	phy_mode = device_get_phy_mode(&pdev->dev);
-	if (phy_mode < 0) {
-		dev_err(&pdev->dev, "phy_mode not found\n");
-		ret = phy_mode;
-		goto err_disable_device;
-	}
-
-	plat->phy_interface = phy_mode;
-
 	pci_set_master(pdev);
 
 	info = (struct stmmac_pci_info *)id->driver_data;
@@ -132,7 +549,6 @@  static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
 			dev_info(&pdev->dev, "Found MDIO subnode\n");
 			plat->mdio_bus_data->needs_reset = true;
 		}
-
 		bus_id = of_alias_get_id(np, "ethernet");
 		if (bus_id >= 0)
 			plat->bus_id = bus_id;
@@ -144,42 +560,43 @@  static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
 			goto err_disable_device;
 		}
 		plat->phy_interface = phy_mode;
-
-		res.irq = of_irq_get_byname(np, "macirq");
-		if (res.irq < 0) {
-			dev_err(&pdev->dev, "IRQ macirq not found\n");
-			ret = -ENODEV;
-			goto err_disable_msi;
-		}
-
-		res.wol_irq = of_irq_get_byname(np, "eth_wake_irq");
-		if (res.wol_irq < 0) {
-			dev_info(&pdev->dev, "IRQ eth_wake_irq not found, using macirq\n");
-			res.wol_irq = res.irq;
-		}
-
-		res.lpi_irq = of_irq_get_byname(np, "eth_lpi");
-		if (res.lpi_irq < 0) {
-			dev_err(&pdev->dev, "IRQ eth_lpi not found\n");
-			ret = -ENODEV;
-			goto err_disable_msi;
-		}
-	} else {
-		res.irq = pdev->irq;
 	}
 
-	pci_enable_msi(pdev);
 	memset(&res, 0, sizeof(res));
 	res.addr = pcim_iomap_table(pdev)[0];
 
+	ld->dev = &pdev->dev;
+	plat->bsp_priv = ld;
+	loongson_gmac = readl(res.addr + GMAC_VERSION) & 0xff;
+
+	switch (loongson_gmac) {
+	case LOONGSON_DWMAC_CORE_1_00:
+		plat->setup = loongson_gnet_setup;
+		/* Only channel 0 of the 0x10 device supports checksum,
+		 * so turn off checksum to enable multiple channels.
+		 */
+		for (i = 1; i < CHANNEL_NUM; i++) {
+			plat->tx_queues_cfg[i].coe_unsupported = 1;
+		};
+		ret = loongson_gnet_config_multi_msi(pdev, plat, &res, np);
+		break;
+	default:	/*0x35 device and 0x37 device.*/
+		ret = loongson_dwmac_config_legacy(pdev, plat, &res, np);
+		break;
+	}
+
+	/* Devices with dev revision 0x00 do not support manually
+	 * setting the speed to 1000.
+	 */
+	if (pdev->revision == 0x00)
+		plat->flags |= STMMAC_FLAG_DISABLE_FORCE_1000;
+
 	ret = stmmac_dvr_probe(&pdev->dev, plat, &res);
 	if (ret)
-		goto err_disable_msi;
+		goto err_disable_device;
 
 	return ret;
 
-err_disable_msi:
-	pci_disable_msi(pdev);
 err_disable_device:
 	pci_disable_device(pdev);
 err_put_node:
@@ -247,6 +664,7 @@  static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend,
 
 static const struct pci_device_id loongson_dwmac_id_table[] = {
 	{ PCI_DEVICE_DATA(LOONGSON, GMAC, &loongson_gmac_pci_info) },
+	{ PCI_DEVICE_DATA(LOONGSON, GNET, &loongson_gnet_pci_info) },
 	{}
 };
 MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index f161ec9ac490..66c0c22908b1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -296,3 +296,4 @@  const struct stmmac_dma_ops dwmac1000_dma_ops = {
 	.get_hw_feature = dwmac1000_get_hw_feature,
 	.rx_watchdog = dwmac1000_rx_watchdog,
 };
+EXPORT_SYMBOL_GPL(dwmac1000_dma_ops);
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 1b54b84a6785..c5d3d0ddb6f8 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -223,6 +223,7 @@  struct dwmac4_addrs {
 #define STMMAC_FLAG_RX_CLK_RUNS_IN_LPI		BIT(10)
 #define STMMAC_FLAG_EN_TX_LPI_CLOCKGATING	BIT(11)
 #define STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY	BIT(12)
+#define STMMAC_FLAG_DISABLE_FORCE_1000		BIT(13)
 
 struct plat_stmmacenet_data {
 	int bus_id;