Message ID | 7ab619c05c2b04fc9552e9f3b995a82f661f339f.1692696115.git.chenfeiyang@loongson.cn (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | stmmac: Add Loongson platform support | expand |
Context | Check | Description |
---|---|---|
netdev/tree_selection | success | Guessing tree name failed - patch did not apply |
On Tue, Aug 22, 2023 at 05:40:28PM +0800, Feiyang Chen wrote: > Loongson platforms use an extended GMAC which supports 64-bit DMA > and multi-channel. > > There are two kinds of Loongson platforms. The first kind shares > the same registers and has similar logic with dwmac1000. The second > kind uses different registers and has more features. > > Add extended GMAC support and then add two HWIF entries for Loongson > platforms. Holy mother! No, no, no! Don't ever do that! Here is what I see: drivers/net/ethernet/stmicro/stmmac/dwegmac.h = drivers/net/ethernet/stmicro/stmmac/dwmac1000.h drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c = drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c = drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c + drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c + DMA channel-specific settings + useless OSR64 update. drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h = drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h + 64-bit address CSRs + useless OSR64 update + DMA channel-specific settings. So you just copied the files from the generic DW GMAC MAC/DMA implementations, altered a few lines there and renamed it to some imaginary DW Extended GMAC. This is a nightmare to review and a dead-end to maintain. Just NO! Here are the notes regarding your device: 1. There is no DW Extended GMAC. The latest DW GMAC IP-core is of v3.73a version. It supports various PHY interfaces and can work with up to 1Gbps Full-duplex speed. 2. Generic DW GMAC 3.73a IP-core can be synthesised with multiple DMA channels support. Such capability is enabled together with the AV feature (Audio/Video feature). There can be up to 2 additional DMA channels enabled. The higher DMA-channels-specific CSRs are available with the 0x100 offset with respect to each other and the Channel 0 base address. Based on what is depicted on the Top-Level Block Diagram of AV Support each DMA-channel is equipped with the MTL Tx/Rx FIFO controller. So basically it's a fixed DMA/MTL-queue setup despite of the DW QoS Eth and DW xGMACs which provide a way to map the MTL-queues to DMA-channels at runtime. BTW Based on the v3.73 IP-core Release notes the AV feature and thus the multi-DMA-channels support was added in the IP-core v3.61a and since then the product with such feature enabled is called as DWC Ethernet QoS. It's mainly compatible with the standard DWC GMAC (DW Ether MAC 10/100/1000 controller) except the DMA-channels and the AV feature setups. And it's absolutely different from what it currently called DW Eth QoS v4.x/v5.x. 3. I failed to find anything about the 64-bit DMA-descriptors address in the v3.73a IP-core databook. So most likely it's indeed either your controller specific feature or a feature implemented by Synopsys for the Loongson vendor but hasn't been released yet. In anyway AFAICS this capability completely blocks the Chained descriptors configuration and seeing it implies the (R|T)DES(6|7) fields utilization the Timestamping will be unavailable too. So your chain_mode.c fixes just convert the chain_mode.c file to implementing the ring mode. That's it. Your "Extended" GMAC isn't that special after all: just 8 DMA channels instead of 2, most likely AV-feature available and the DMA-descriptors supporting 64-bit addresses. Even the MAC and DMA CSRs space is almost the same as can be found in the generic DW GMAC. What you should have done: 1. Update the dwmac1000_dma.c to support the multi-DMA-channels controller setup. 2. Update ring_mode.c to support the 64-bit DMA-descriptor addresses. 3. Prevent the chain-mode from being enabled for your controller. That's it AFAICS for now. But it's definitely no to what is currently implemented. -Serge(y) > > Signed-off-by: Feiyang Chen <chenfeiyang@loongson.cn> > Signed-off-by: Yinggang Gu <guyinggang@loongson.cn> > --- > drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +- > drivers/net/ethernet/stmicro/stmmac/common.h | 2 + > drivers/net/ethernet/stmicro/stmmac/dwegmac.h | 332 +++++++++++ > .../ethernet/stmicro/stmmac/dwegmac_core.c | 552 ++++++++++++++++++ > .../net/ethernet/stmicro/stmmac/dwegmac_dma.c | 516 ++++++++++++++++ > .../net/ethernet/stmicro/stmmac/dwegmac_dma.h | 190 ++++++ > drivers/net/ethernet/stmicro/stmmac/hwif.c | 54 +- > drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 + > .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +- > .../net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +- > include/linux/stmmac.h | 1 + > 11 files changed, 1651 insertions(+), 5 deletions(-) > create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwegmac.h > create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c > create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c > create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h > > diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile > index 10f32ded2bd9..1238cd736910 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/Makefile > +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile > @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ > mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \ > dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \ > stmmac_tc.o dwxgmac2_core.o dwxgmac2_dma.o dwxgmac2_descs.o \ > - stmmac_xdp.o ring_mode64.o \ > + stmmac_xdp.o ring_mode64.o dwegmac_core.o dwegmac_dma.o \ > $(stmmac-y) > > stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o > diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h > index 90a7784f71cb..74528a16b93a 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/common.h > +++ b/drivers/net/ethernet/stmicro/stmmac/common.h > @@ -36,6 +36,7 @@ > #define DWMAC_CORE_5_20 0x52 > #define DWXGMAC_CORE_2_10 0x21 > #define DWXLGMAC_CORE_2_00 0x20 > +#define DWEGMAC_CORE_1_00 0x10 > > /* Device ID */ > #define DWXGMAC_ID 0x76 > @@ -547,6 +548,7 @@ int dwmac1000_setup(struct stmmac_priv *priv); > int dwmac4_setup(struct stmmac_priv *priv); > int dwxgmac2_setup(struct stmmac_priv *priv); > int dwxlgmac2_setup(struct stmmac_priv *priv); > +int dwegmac_setup(struct stmmac_priv *priv); > > void stmmac_set_mac_addr(void __iomem *ioaddr, const u8 addr[6], > unsigned int high, unsigned int low); > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac.h b/drivers/net/ethernet/stmicro/stmmac/dwegmac.h > new file mode 100644 > index 000000000000..6f8fcf3ed409 > --- /dev/null > +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac.h > @@ -0,0 +1,332 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#ifndef __DWEGMAC_H__ > +#define __DWEGMAC_H__ > + > +#include <linux/phy.h> > +#include "common.h" > + > +#define GMAC_CONTROL 0x00000000 /* Configuration */ > +#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */ > +#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */ > +#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */ > +#define GMAC_MII_ADDR 0x00000010 /* MII Address */ > +#define GMAC_MII_DATA 0x00000014 /* MII Data */ > +#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */ > +#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */ > +#define GMAC_DEBUG 0x00000024 /* GMAC debug register */ > +#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ > + > +#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ > +#define GMAC_INT_STATUS_PMT BIT(3) > +#define GMAC_INT_STATUS_MMCIS BIT(4) > +#define GMAC_INT_STATUS_MMCRIS BIT(5) > +#define GMAC_INT_STATUS_MMCTIS BIT(6) > +#define GMAC_INT_STATUS_MMCCSUM BIT(7) > +#define GMAC_INT_STATUS_TSTAMP BIT(9) > +#define GMAC_INT_STATUS_LPIIS BIT(10) > + > +/* interrupt mask register */ > +#define GMAC_INT_MASK 0x0000003c > +#define GMAC_INT_DISABLE_RGMII BIT(0) > +#define GMAC_INT_DISABLE_PCSLINK BIT(1) > +#define GMAC_INT_DISABLE_PCSAN BIT(2) > +#define GMAC_INT_DISABLE_PMT BIT(3) > +#define GMAC_INT_DISABLE_TIMESTAMP BIT(9) > +#define GMAC_INT_DISABLE_PCS (GMAC_INT_DISABLE_RGMII | \ > + GMAC_INT_DISABLE_PCSLINK | \ > + GMAC_INT_DISABLE_PCSAN) > +#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_TIMESTAMP | \ > + GMAC_INT_DISABLE_PCS) > + > +/* PMT Control and Status */ > +#define GMAC_PMT 0x0000002c > +enum power_event { > + pointer_reset = 0x80000000, > + global_unicast = 0x00000200, > + wake_up_rx_frame = 0x00000040, > + magic_frame = 0x00000020, > + wake_up_frame_en = 0x00000004, > + magic_pkt_en = 0x00000002, > + power_down = 0x00000001, > +}; > + > +/* Energy Efficient Ethernet (EEE) > + * > + * LPI status, timer and control register offset > + */ > +#define LPI_CTRL_STATUS 0x0030 > +#define LPI_TIMER_CTRL 0x0034 > + > +/* LPI control and status defines */ > +#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */ > +#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */ > +#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */ > +#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */ > +#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */ > +#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */ > +#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */ > +#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */ > +#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */ > +#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */ > + > +/* GMAC HW ADDR regs */ > +#define GMAC_ADDR_HIGH(reg) ((reg > 15) ? 0x00000800 + (reg - 16) * 8 : \ > + 0x00000040 + (reg * 8)) > +#define GMAC_ADDR_LOW(reg) ((reg > 15) ? 0x00000804 + (reg - 16) * 8 : \ > + 0x00000044 + (reg * 8)) > +#define GMAC_MAX_PERFECT_ADDRESSES 1 > + > +#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */ > +#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */ > + > +/* SGMII/RGMII status register */ > +#define GMAC_RGSMIIIS_LNKMODE BIT(0) > +#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1) > +#define GMAC_RGSMIIIS_SPEED_SHIFT 1 > +#define GMAC_RGSMIIIS_LNKSTS BIT(3) > +#define GMAC_RGSMIIIS_JABTO BIT(4) > +#define GMAC_RGSMIIIS_FALSECARDET BIT(5) > +#define GMAC_RGSMIIIS_SMIDRXS BIT(16) > +/* LNKMOD */ > +#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1 > +/* LNKSPEED */ > +#define GMAC_RGSMIIIS_SPEED_125 0x2 > +#define GMAC_RGSMIIIS_SPEED_25 0x1 > +#define GMAC_RGSMIIIS_SPEED_2_5 0x0 > + > +/* GMAC Configuration defines */ > +#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */ > +#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */ > +#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */ > +#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */ > +#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */ > +#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ > +enum inter_frame_gap { > + GMAC_CONTROL_IFG_88 = 0x00040000, > + GMAC_CONTROL_IFG_80 = 0x00020000, > + GMAC_CONTROL_IFG_40 = 0x000e0000, > +}; > +#define GMAC_CONTROL_DCRS 0x00010000 /* Disable carrier sense */ > +#define GMAC_CONTROL_PS 0x00008000 /* Port Select 0:GMI 1:MII */ > +#define GMAC_CONTROL_FES 0x00004000 /* Speed 0:10 1:100 */ > +#define GMAC_CONTROL_DO 0x00002000 /* Disable Rx Own */ > +#define GMAC_CONTROL_LM 0x00001000 /* Loop-back mode */ > +#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */ > +#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */ > +#define GMAC_CONTROL_DR 0x00000200 /* Disable Retry */ > +#define GMAC_CONTROL_LUD 0x00000100 /* Link up/down */ > +#define GMAC_CONTROL_ACS 0x00000080 /* Auto Pad/FCS Stripping */ > +#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */ > +#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ > +#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ > + > +#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | \ > + GMAC_CONTROL_BE | GMAC_CONTROL_DCRS | \ > + GMAC_CONTROL_ACS) > + > +/* GMAC Frame Filter defines */ > +#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */ > +#define GMAC_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */ > +#define GMAC_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */ > +#define GMAC_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */ > +#define GMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */ > +#define GMAC_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */ > +#define GMAC_FRAME_FILTER_PCF 0x00000080 /* Pass Control frames */ > +#define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ > +#define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ > +#define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ > +#define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ > +/* GMII ADDR defines */ > +#define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ > +#define GMAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */ > +/* GMAC FLOW CTRL defines */ > +#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ > +#define GMAC_FLOW_CTRL_PT_SHIFT 16 > +#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */ > +#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ > +#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ > +#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ > + > +/* DEBUG Register defines */ > +/* MTL TxStatus FIFO */ > +#define GMAC_DEBUG_TXSTSFSTS BIT(25) /* MTL TxStatus FIFO Full Status */ > +#define GMAC_DEBUG_TXFSTS BIT(24) /* MTL Tx FIFO Not Empty Status */ > +#define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */ > +/* MTL Tx FIFO Read Controller Status */ > +#define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20) > +#define GMAC_DEBUG_TRCSTS_SHIFT 20 > +#define GMAC_DEBUG_TRCSTS_IDLE 0 > +#define GMAC_DEBUG_TRCSTS_READ 1 > +#define GMAC_DEBUG_TRCSTS_TXW 2 > +#define GMAC_DEBUG_TRCSTS_WRITE 3 > +#define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */ > +/* MAC Transmit Frame Controller Status */ > +#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17) > +#define GMAC_DEBUG_TFCSTS_SHIFT 17 > +#define GMAC_DEBUG_TFCSTS_IDLE 0 > +#define GMAC_DEBUG_TFCSTS_WAIT 1 > +#define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2 > +#define GMAC_DEBUG_TFCSTS_XFER 3 > +/* MAC GMII or MII Transmit Protocol Engine Status */ > +#define GMAC_DEBUG_TPESTS BIT(16) > +#define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */ > +#define GMAC_DEBUG_RXFSTS_SHIFT 8 > +#define GMAC_DEBUG_RXFSTS_EMPTY 0 > +#define GMAC_DEBUG_RXFSTS_BT 1 > +#define GMAC_DEBUG_RXFSTS_AT 2 > +#define GMAC_DEBUG_RXFSTS_FULL 3 > +#define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */ > +#define GMAC_DEBUG_RRCSTS_SHIFT 5 > +#define GMAC_DEBUG_RRCSTS_IDLE 0 > +#define GMAC_DEBUG_RRCSTS_RDATA 1 > +#define GMAC_DEBUG_RRCSTS_RSTAT 2 > +#define GMAC_DEBUG_RRCSTS_FLUSH 3 > +#define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */ > +/* MAC Receive Frame Controller FIFO Status */ > +#define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1) > +#define GMAC_DEBUG_RFCFCSTS_SHIFT 1 > +/* MAC GMII or MII Receive Protocol Engine Status */ > +#define GMAC_DEBUG_RPESTS BIT(0) > + > +/*--- DMA BLOCK defines ---*/ > +/* DMA Bus Mode register defines */ > +#define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */ > +#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ > +#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ > +/* Programmable burst length (passed thorugh platform)*/ > +#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ > +#define DMA_BUS_MODE_PBL_SHIFT 8 > +#define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */ > + > +enum rx_tx_priority_ratio { > + double_ratio = 0x00004000, /* 2:1 */ > + triple_ratio = 0x00008000, /* 3:1 */ > + quadruple_ratio = 0x0000c000, /* 4:1 */ > +}; > + > +#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ > +#define DMA_BUS_MODE_MB 0x04000000 /* Mixed burst */ > +#define DMA_BUS_MODE_RPBL_MASK 0x007e0000 /* Rx-Programmable Burst Len */ > +#define DMA_BUS_MODE_RPBL_SHIFT 17 > +#define DMA_BUS_MODE_USP 0x00800000 > +#define DMA_BUS_MODE_MAXPBL 0x01000000 > +#define DMA_BUS_MODE_AAL 0x02000000 > + > +/* DMA CRS Control and Status Register Mapping */ > +#define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */ > +#define DMA_HOST_RX_DESC 0x0000104c /* Current Host Rx descriptor */ > +/* DMA Bus Mode register defines */ > +#define DMA_BUS_PR_RATIO_MASK 0x0000c000 /* Rx/Tx priority ratio */ > +#define DMA_BUS_PR_RATIO_SHIFT 14 > +#define DMA_BUS_FB 0x00010000 /* Fixed Burst */ > + > +/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/ > +/* Disable Drop TCP/IP csum error */ > +#define DMA_CONTROL_DT 0x04000000 > +#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */ > +#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */ > +/* Threshold for Activating the FC */ > +enum rfa { > + act_full_minus_1 = 0x00800000, > + act_full_minus_2 = 0x00800200, > + act_full_minus_3 = 0x00800400, > + act_full_minus_4 = 0x00800600, > +}; > +/* Threshold for Deactivating the FC */ > +enum rfd { > + deac_full_minus_1 = 0x00400000, > + deac_full_minus_2 = 0x00400800, > + deac_full_minus_3 = 0x00401000, > + deac_full_minus_4 = 0x00401800, > +}; > +#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */ > + > +enum ttc_control { > + DMA_CONTROL_TTC_64 = 0x00000000, > + DMA_CONTROL_TTC_128 = 0x00004000, > + DMA_CONTROL_TTC_192 = 0x00008000, > + DMA_CONTROL_TTC_256 = 0x0000c000, > + DMA_CONTROL_TTC_40 = 0x00010000, > + DMA_CONTROL_TTC_32 = 0x00014000, > + DMA_CONTROL_TTC_24 = 0x00018000, > + DMA_CONTROL_TTC_16 = 0x0001c000, > +}; > +#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff > + > +#define DMA_CONTROL_EFC 0x00000100 > +#define DMA_CONTROL_FEF 0x00000080 > +#define DMA_CONTROL_FUF 0x00000040 > + > +/* Receive flow control activation field > + * RFA field in DMA control register, bits 23,10:9 > + */ > +#define DMA_CONTROL_RFA_MASK 0x00800600 > + > +/* Receive flow control deactivation field > + * RFD field in DMA control register, bits 22,12:11 > + */ > +#define DMA_CONTROL_RFD_MASK 0x00401800 > + > +/* RFD and RFA fields are encoded as follows > + * > + * Bit Field > + * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled) > + * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled) > + * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled) > + * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled) > + * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled) > + * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled) > + * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled) > + * 1,11 - Reserved > + * > + * RFD should always be > RFA for a given FIFO size. RFD == RFA may work, > + * but packet throughput performance may not be as expected. > + * > + * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame > + * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause > + * Description). > + * > + * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6), > + * is set to 0. This allows pause frames with a quanta of 0 to be sent > + * as an XOFF message to the link peer. > + */ > + > +#define RFA_FULL_MINUS_1K 0x00000000 > +#define RFA_FULL_MINUS_2K 0x00000200 > +#define RFA_FULL_MINUS_3K 0x00000400 > +#define RFA_FULL_MINUS_4K 0x00000600 > +#define RFA_FULL_MINUS_5K 0x00800000 > +#define RFA_FULL_MINUS_6K 0x00800200 > +#define RFA_FULL_MINUS_7K 0x00800400 > + > +#define RFD_FULL_MINUS_1K 0x00000000 > +#define RFD_FULL_MINUS_2K 0x00000800 > +#define RFD_FULL_MINUS_3K 0x00001000 > +#define RFD_FULL_MINUS_4K 0x00001800 > +#define RFD_FULL_MINUS_5K 0x00400000 > +#define RFD_FULL_MINUS_6K 0x00400800 > +#define RFD_FULL_MINUS_7K 0x00401000 > + > +enum rtc_control { > + DMA_CONTROL_RTC_64 = 0x00000000, > + DMA_CONTROL_RTC_32 = 0x00000008, > + DMA_CONTROL_RTC_96 = 0x00000010, > + DMA_CONTROL_RTC_128 = 0x00000018, > +}; > +#define DMA_CONTROL_TC_RX_MASK 0xffffffe7 > + > +#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */ > + > +/* MMC registers offset */ > +#define GMAC_MMC_CTRL 0x100 > +#define GMAC_MMC_RX_INTR 0x104 > +#define GMAC_MMC_TX_INTR 0x108 > +#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 > +#define GMAC_EXTHASH_BASE 0x500 > + > +extern const struct stmmac_dma_ops dwegmac_dma_ops; > +#endif /* __DWEGMAC_H__ */ > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c b/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c > new file mode 100644 > index 000000000000..4e0c8731adfb > --- /dev/null > +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c > @@ -0,0 +1,552 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include <linux/crc32.h> > +#include <linux/slab.h> > +#include <linux/ethtool.h> > +#include <linux/io.h> > +#include "stmmac.h" > +#include "stmmac_pcs.h" > +#include "dwegmac.h" > + > +static void dwegmac_core_init(struct mac_device_info *hw, > + struct net_device *dev) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 value = readl(ioaddr + GMAC_CONTROL); > + int mtu = dev->mtu; > + > + /* Configure GMAC core */ > + value |= GMAC_CORE_INIT; > + > + if (mtu > 1500) > + value |= GMAC_CONTROL_2K; > + if (mtu > 2000) > + value |= GMAC_CONTROL_JE; > + > + if (hw->ps) { > + value |= GMAC_CONTROL_TE; > + > + value &= ~hw->link.speed_mask; > + switch (hw->ps) { > + case SPEED_1000: > + value |= hw->link.speed1000; > + break; > + case SPEED_100: > + value |= hw->link.speed100; > + break; > + case SPEED_10: > + value |= hw->link.speed10; > + break; > + } > + } > + > + writel(value, ioaddr + GMAC_CONTROL); > + > + /* Mask GMAC interrupts */ > + value = GMAC_INT_DEFAULT_MASK; > + > + if (hw->pcs) > + value &= ~GMAC_INT_DISABLE_PCS; > + > + writel(value, ioaddr + GMAC_INT_MASK); > + > +#ifdef STMMAC_VLAN_TAG_USED > + /* Tag detection without filtering */ > + writel(0x0, ioaddr + GMAC_VLAN_TAG); > +#endif > +} > + > +static int dwegmac_rx_ipc_enable(struct mac_device_info *hw) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 value = readl(ioaddr + GMAC_CONTROL); > + > + if (hw->rx_csum) > + value |= GMAC_CONTROL_IPC; > + else > + value &= ~GMAC_CONTROL_IPC; > + > + writel(value, ioaddr + GMAC_CONTROL); > + > + value = readl(ioaddr + GMAC_CONTROL); > + > + return !!(value & GMAC_CONTROL_IPC); > +} > + > +static void dwegmac_dump_regs(struct mac_device_info *hw, u32 *reg_space) > +{ > + void __iomem *ioaddr = hw->pcsr; > + int i; > + > + for (i = 0; i < 55; i++) > + reg_space[i] = readl(ioaddr + i * 4); > +} > + > +static void dwegmac_set_umac_addr(struct stmmac_priv *priv, > + struct mac_device_info *hw, > + const unsigned char *addr, > + unsigned int reg_n) > +{ > + void __iomem *ioaddr = hw->pcsr; > + > + stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), > + GMAC_ADDR_LOW(reg_n)); > +} > + > +static void dwegmac_get_umac_addr(struct stmmac_priv *priv, > + struct mac_device_info *hw, > + unsigned char *addr, > + unsigned int reg_n) > +{ > + void __iomem *ioaddr = hw->pcsr; > + > + stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), > + GMAC_ADDR_LOW(reg_n)); > +} > + > +static void dwegmac_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits, > + int mcbitslog2) > +{ > + int numhashregs, regs; > + > + switch (mcbitslog2) { > + case 6: > + writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW); > + writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH); > + return; > + case 7: > + numhashregs = 4; > + break; > + case 8: > + numhashregs = 8; > + break; > + default: > + pr_debug("STMMAC: err in setting multicast filter\n"); > + return; > + } > + for (regs = 0; regs < numhashregs; regs++) > + writel(mcfilterbits[regs], > + ioaddr + GMAC_EXTHASH_BASE + regs * 4); > +} > + > +static void dwegmac_set_filter(struct stmmac_priv *priv, > + struct mac_device_info *hw, > + struct net_device *dev) > +{ > + void __iomem *ioaddr = (void __iomem *)dev->base_addr; > + unsigned int value = 0; > + unsigned int perfect_addr_number = hw->unicast_filter_entries; > + u32 mc_filter[8]; > + int mcbitslog2 = hw->mcast_bits_log2; > + > + pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, > + netdev_mc_count(dev), netdev_uc_count(dev)); > + > + memset(mc_filter, 0, sizeof(mc_filter)); > + > + if (dev->flags & IFF_PROMISC) { > + value = GMAC_FRAME_FILTER_PR | GMAC_FRAME_FILTER_PCF; > + } else if (dev->flags & IFF_ALLMULTI) { > + value = GMAC_FRAME_FILTER_PM; /* pass all multi */ > + } else if (!netdev_mc_empty(dev) && (mcbitslog2 == 0)) { > + /* Fall back to all multicast if we've no filter */ > + value = GMAC_FRAME_FILTER_PM; > + } else if (!netdev_mc_empty(dev)) { > + struct netdev_hw_addr *ha; > + > + /* Hash filter for multicast */ > + value = GMAC_FRAME_FILTER_HMC; > + > + netdev_for_each_mc_addr(ha, dev) { > + /* The upper n bits of the calculated CRC are used to > + * index the contents of the hash table. The number of > + * bits used depends on the hardware configuration > + * selected at core configuration time. > + */ > + int bit_nr = bitrev32(~crc32_le(~0, ha->addr, > + ETH_ALEN)) >> > + (32 - mcbitslog2); > + /* The most significant bit determines the register to > + * use (H/L) while the other 5 bits determine the bit > + * within the register. > + */ > + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); > + } > + } > + > + value |= GMAC_FRAME_FILTER_HPF; > + dwegmac_set_mchash(ioaddr, mc_filter, mcbitslog2); > + > + /* Handle multiple unicast addresses (perfect filtering) */ > + if (netdev_uc_count(dev) > perfect_addr_number) { > + /* Switch to promiscuous mode if more than unicast > + * addresses are requested than supported by hardware. > + */ > + value |= GMAC_FRAME_FILTER_PR; > + } else { > + int reg = 1; > + struct netdev_hw_addr *ha; > + > + netdev_for_each_uc_addr(ha, dev) { > + stmmac_set_mac_addr(ioaddr, ha->addr, > + GMAC_ADDR_HIGH(reg), > + GMAC_ADDR_LOW(reg)); > + reg++; > + } > + > + while (reg < perfect_addr_number) { > + writel(0, ioaddr + GMAC_ADDR_HIGH(reg)); > + writel(0, ioaddr + GMAC_ADDR_LOW(reg)); > + reg++; > + } > + } > + > +#ifdef FRAME_FILTER_DEBUG > + /* Enable Receive all mode (to debug filtering_fail errors) */ > + value |= GMAC_FRAME_FILTER_RA; > +#endif > + writel(value, ioaddr + GMAC_FRAME_FILTER); > +} > + > +static void dwegmac_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, > + unsigned int fc, unsigned int pause_time, > + u32 tx_cnt) > +{ > + void __iomem *ioaddr = hw->pcsr; > + /* Set flow such that DZPQ in Mac Register 6 is 0, > + * and unicast pause detect is enabled. > + */ > + unsigned int flow = GMAC_FLOW_CTRL_UP; > + > + pr_debug("GMAC Flow-Control:\n"); > + if (fc & FLOW_RX) { > + pr_debug("\tReceive Flow-Control ON\n"); > + flow |= GMAC_FLOW_CTRL_RFE; > + } > + if (fc & FLOW_TX) { > + pr_debug("\tTransmit Flow-Control ON\n"); > + flow |= GMAC_FLOW_CTRL_TFE; > + } > + > + if (duplex) { > + pr_debug("\tduplex mode: PAUSE %d\n", pause_time); > + flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); > + } > + > + writel(flow, ioaddr + GMAC_FLOW_CTRL); > +} > + > +static void dwegmac_pmt(struct mac_device_info *hw, unsigned long mode) > +{ > + void __iomem *ioaddr = hw->pcsr; > + unsigned int pmt = 0; > + > + if (mode & WAKE_MAGIC) { > + pr_debug("GMAC: WOL Magic frame\n"); > + pmt |= power_down | magic_pkt_en; > + } > + if (mode & WAKE_UCAST) { > + pr_debug("GMAC: WOL on global unicast\n"); > + pmt |= power_down | global_unicast | wake_up_frame_en; > + } > + > + writel(pmt, ioaddr + GMAC_PMT); > +} > + > +/* RGMII or SMII interface */ > +static void dwegmac_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x) > +{ > + u32 status; > + > + status = readl(ioaddr + GMAC_RGSMIIIS); > + x->irq_rgmii_n++; > + > + /* Check the link status */ > + if (status & GMAC_RGSMIIIS_LNKSTS) { > + int speed_value; > + > + x->pcs_link = 1; > + > + speed_value = ((status & GMAC_RGSMIIIS_SPEED) >> > + GMAC_RGSMIIIS_SPEED_SHIFT); > + if (speed_value == GMAC_RGSMIIIS_SPEED_125) > + x->pcs_speed = SPEED_1000; > + else if (speed_value == GMAC_RGSMIIIS_SPEED_25) > + x->pcs_speed = SPEED_100; > + else > + x->pcs_speed = SPEED_10; > + > + x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK); > + > + pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, > + x->pcs_duplex ? "Full" : "Half"); > + } else { > + x->pcs_link = 0; > + pr_info("Link is Down\n"); > + } > +} > + > +static int dwegmac_irq_status(struct mac_device_info *hw, > + struct stmmac_extra_stats *x) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); > + u32 intr_mask = readl(ioaddr + GMAC_INT_MASK); > + int ret = 0; > + > + /* Discard masked bits */ > + intr_status &= ~intr_mask; > + > + /* Not used events (e.g. MMC interrupts) are not handled. */ > + if ((intr_status & GMAC_INT_STATUS_MMCTIS)) > + x->mmc_tx_irq_n++; > + if (unlikely(intr_status & GMAC_INT_STATUS_MMCRIS)) > + x->mmc_rx_irq_n++; > + if (unlikely(intr_status & GMAC_INT_STATUS_MMCCSUM)) > + x->mmc_rx_csum_offload_irq_n++; > + if (unlikely(intr_status & GMAC_INT_DISABLE_PMT)) { > + /* clear the PMT bits 5 and 6 by reading the PMT status reg */ > + readl(ioaddr + GMAC_PMT); > + x->irq_receive_pmt_irq_n++; > + } > + > + /* MAC tx/rx EEE LPI entry/exit interrupts */ > + if (intr_status & GMAC_INT_STATUS_LPIIS) { > + /* Clean LPI interrupt by reading the Reg 12 */ > + ret = readl(ioaddr + LPI_CTRL_STATUS); > + > + if (ret & LPI_CTRL_STATUS_TLPIEN) > + x->irq_tx_path_in_lpi_mode_n++; > + if (ret & LPI_CTRL_STATUS_TLPIEX) > + x->irq_tx_path_exit_lpi_mode_n++; > + if (ret & LPI_CTRL_STATUS_RLPIEN) > + x->irq_rx_path_in_lpi_mode_n++; > + if (ret & LPI_CTRL_STATUS_RLPIEX) > + x->irq_rx_path_exit_lpi_mode_n++; > + } > + > + dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x); > + > + if (intr_status & PCS_RGSMIIIS_IRQ) > + dwegmac_rgsmii(ioaddr, x); > + > + return ret; > +} > + > +static void dwegmac_set_eee_mode(struct mac_device_info *hw, > + bool en_tx_lpi_clockgating) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 value; > + > + /*TODO - en_tx_lpi_clockgating treatment */ > + > + /* Enable the link status receive on RGMII, SGMII ore SMII > + * receive path and instruct the transmit to enter in LPI > + * state. > + */ > + value = readl(ioaddr + LPI_CTRL_STATUS); > + value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA; > + writel(value, ioaddr + LPI_CTRL_STATUS); > +} > + > +static void dwegmac_reset_eee_mode(struct mac_device_info *hw) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 value; > + > + value = readl(ioaddr + LPI_CTRL_STATUS); > + value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA); > + writel(value, ioaddr + LPI_CTRL_STATUS); > +} > + > +static void dwegmac_set_eee_pls(struct mac_device_info *hw, int link) > +{ > + void __iomem *ioaddr = hw->pcsr; > + u32 value; > + > + value = readl(ioaddr + LPI_CTRL_STATUS); > + > + if (link) > + value |= LPI_CTRL_STATUS_PLS; > + else > + value &= ~LPI_CTRL_STATUS_PLS; > + > + writel(value, ioaddr + LPI_CTRL_STATUS); > +} > + > +static void dwegmac_set_eee_timer(struct mac_device_info *hw, int ls, int tw) > +{ > + void __iomem *ioaddr = hw->pcsr; > + int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16); > + > + /* Program the timers in the LPI timer control register: > + * LS: minimum time (ms) for which the link > + * status from PHY should be ok before transmitting > + * the LPI pattern. > + * TW: minimum time (us) for which the core waits > + * after it has stopped transmitting the LPI pattern. > + */ > + writel(value, ioaddr + LPI_TIMER_CTRL); > +} > + > +static void dwegmac_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral, > + bool loopback) > +{ > + dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback); > +} > + > +static void dwegmac_rane(void __iomem *ioaddr, bool restart) > +{ > + dwmac_rane(ioaddr, GMAC_PCS_BASE, restart); > +} > + > +static void dwegmac_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv) > +{ > + dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv); > +} > + > +static void dwegmac_debug(struct stmmac_priv *priv, void __iomem *ioaddr, > + struct stmmac_extra_stats *x, > + u32 rx_queues, u32 tx_queues) > +{ > + u32 value = readl(ioaddr + GMAC_DEBUG); > + > + if (value & GMAC_DEBUG_TXSTSFSTS) > + x->mtl_tx_status_fifo_full++; > + if (value & GMAC_DEBUG_TXFSTS) > + x->mtl_tx_fifo_not_empty++; > + if (value & GMAC_DEBUG_TWCSTS) > + x->mmtl_fifo_ctrl++; > + if (value & GMAC_DEBUG_TRCSTS_MASK) { > + u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK) > + >> GMAC_DEBUG_TRCSTS_SHIFT; > + if (trcsts == GMAC_DEBUG_TRCSTS_WRITE) > + x->mtl_tx_fifo_read_ctrl_write++; > + else if (trcsts == GMAC_DEBUG_TRCSTS_TXW) > + x->mtl_tx_fifo_read_ctrl_wait++; > + else if (trcsts == GMAC_DEBUG_TRCSTS_READ) > + x->mtl_tx_fifo_read_ctrl_read++; > + else > + x->mtl_tx_fifo_read_ctrl_idle++; > + } > + if (value & GMAC_DEBUG_TXPAUSED) > + x->mac_tx_in_pause++; > + if (value & GMAC_DEBUG_TFCSTS_MASK) { > + u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK) > + >> GMAC_DEBUG_TFCSTS_SHIFT; > + > + if (tfcsts == GMAC_DEBUG_TFCSTS_XFER) > + x->mac_tx_frame_ctrl_xfer++; > + else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE) > + x->mac_tx_frame_ctrl_pause++; > + else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT) > + x->mac_tx_frame_ctrl_wait++; > + else > + x->mac_tx_frame_ctrl_idle++; > + } > + if (value & GMAC_DEBUG_TPESTS) > + x->mac_gmii_tx_proto_engine++; > + if (value & GMAC_DEBUG_RXFSTS_MASK) { > + u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK) > + >> GMAC_DEBUG_RRCSTS_SHIFT; > + > + if (rxfsts == GMAC_DEBUG_RXFSTS_FULL) > + x->mtl_rx_fifo_fill_level_full++; > + else if (rxfsts == GMAC_DEBUG_RXFSTS_AT) > + x->mtl_rx_fifo_fill_above_thresh++; > + else if (rxfsts == GMAC_DEBUG_RXFSTS_BT) > + x->mtl_rx_fifo_fill_below_thresh++; > + else > + x->mtl_rx_fifo_fill_level_empty++; > + } > + if (value & GMAC_DEBUG_RRCSTS_MASK) { > + u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >> > + GMAC_DEBUG_RRCSTS_SHIFT; > + > + if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH) > + x->mtl_rx_fifo_read_ctrl_flush++; > + else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT) > + x->mtl_rx_fifo_read_ctrl_read_data++; > + else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA) > + x->mtl_rx_fifo_read_ctrl_status++; > + else > + x->mtl_rx_fifo_read_ctrl_idle++; > + } > + if (value & GMAC_DEBUG_RWCSTS) > + x->mtl_rx_fifo_ctrl_active++; > + if (value & GMAC_DEBUG_RFCFCSTS_MASK) > + x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK) > + >> GMAC_DEBUG_RFCFCSTS_SHIFT; > + if (value & GMAC_DEBUG_RPESTS) > + x->mac_gmii_rx_proto_engine++; > +} > + > +static void dwegmac_set_mac_loopback(void __iomem *ioaddr, bool enable) > +{ > + u32 value = readl(ioaddr + GMAC_CONTROL); > + > + if (enable) > + value |= GMAC_CONTROL_LM; > + else > + value &= ~GMAC_CONTROL_LM; > + > + writel(value, ioaddr + GMAC_CONTROL); > +} > + > +const struct stmmac_ops dwegmac_ops = { > + .core_init = dwegmac_core_init, > + .set_mac = stmmac_set_mac, > + .rx_ipc = dwegmac_rx_ipc_enable, > + .dump_regs = dwegmac_dump_regs, > + .host_irq_status = dwegmac_irq_status, > + .set_filter = dwegmac_set_filter, > + .flow_ctrl = dwegmac_flow_ctrl, > + .pmt = dwegmac_pmt, > + .set_umac_addr = dwegmac_set_umac_addr, > + .get_umac_addr = dwegmac_get_umac_addr, > + .set_eee_mode = dwegmac_set_eee_mode, > + .reset_eee_mode = dwegmac_reset_eee_mode, > + .set_eee_timer = dwegmac_set_eee_timer, > + .set_eee_pls = dwegmac_set_eee_pls, > + .debug = dwegmac_debug, > + .pcs_ctrl_ane = dwegmac_ctrl_ane, > + .pcs_rane = dwegmac_rane, > + .pcs_get_adv_lp = dwegmac_get_adv_lp, > + .set_mac_loopback = dwegmac_set_mac_loopback, > +}; > + > +int dwegmac_setup(struct stmmac_priv *priv) > +{ > + struct mac_device_info *mac = priv->hw; > + > + dev_info(priv->device, "\tExtended DWMAC\n"); > + > + priv->dev->priv_flags |= IFF_UNICAST_FLT; > + 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 0; > +} > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c > new file mode 100644 > index 000000000000..9bb0564fbeff > --- /dev/null > +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c > @@ -0,0 +1,516 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#include <linux/io.h> > +#include "stmmac.h" > +#include "dwegmac.h" > +#include "dwegmac_dma.h" > + > +static int dwegmac_dma_reset(struct stmmac_priv *priv, void __iomem *ioaddr) > +{ > + u32 value = readl(ioaddr + DMA_BUS_MODE); > + > + value |= DMA_BUS_MODE_SFT_RESET; > + writel(value, ioaddr + DMA_BUS_MODE); > + > + return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, > + !(value & DMA_BUS_MODE_SFT_RESET), > + 10000, 200000); > +} > + > +static void dwegmac_enable_dma_transmission(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 chan) > +{ > + writel(1, ioaddr + DMA_CHAN_XMT_POLL_DEMAND(chan)); > +} > + > +static void dwegmac_enable_dma_irq(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + u32 chan, bool rx, bool tx) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); > + > + if (rx) > + value |= DMA_INTR_DEFAULT_RX; > + if (tx) > + value |= DMA_INTR_DEFAULT_TX; > + > + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); > +} > + > +static void dwegmac_disable_dma_irq(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + u32 chan, bool rx, bool tx) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); > + > + if (rx) > + value &= ~DMA_INTR_DEFAULT_RX; > + if (tx) > + value &= ~DMA_INTR_DEFAULT_TX; > + > + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); > +} > + > +static void dwegmac_dma_start_tx(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 chan) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); > + > + value |= DMA_CONTROL_ST; > + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); > +} > + > +static void dwegmac_dma_stop_tx(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 chan) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); > + > + value &= ~DMA_CONTROL_ST; > + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); > +} > + > +static void dwegmac_dma_start_rx(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 chan) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); > + > + value |= DMA_CONTROL_SR; > + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); > +} > + > +static void dwegmac_dma_stop_rx(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 chan) > +{ > + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); > + > + value &= ~DMA_CONTROL_SR; > + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); > +} > + > +static int dwegmac_dma_interrupt(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + struct stmmac_extra_stats *x, > + u32 chan, u32 dir) > +{ > + int ret = 0; > + u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(chan)); > + > + if (dir == DMA_DIR_RX) > + intr_status &= DMA_STATUS_MSK_RX; > + else if (dir == DMA_DIR_TX) > + intr_status &= DMA_STATUS_MSK_TX; > + > + /* ABNORMAL interrupts */ > + if (unlikely(intr_status & (DMA_STATUS_TX_AIS | DMA_STATUS_RX_AIS))) { > + 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 & > + (DMA_STATUS_TX_FBI | DMA_STATUS_RX_FBI))) { > + x->fatal_bus_error_irq++; > + ret = tx_hard_error; > + } > + } > + /* TX/RX NORMAL interrupts */ > + if (likely(intr_status & (DMA_STATUS_TX_NIS | DMA_STATUS_RX_NIS))) { > + x->normal_irq_n++; > + 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)) { > + x->rx_normal_irq_n++; > + ret |= handle_rx; > + } > + } > + if (likely(intr_status & DMA_STATUS_TI)) { > + x->tx_normal_irq_n++; > + ret |= handle_tx; > + } > + if (unlikely(intr_status & DMA_STATUS_ERI)) > + x->rx_early_irq++; > + } > + > + writel((intr_status & 0x7ffff), ioaddr + DMA_CHAN_STATUS(chan)); > + > + return ret; > +} > + > +static void dwegmac_dma_axi(struct stmmac_priv *priv, void __iomem *ioaddr, > + struct stmmac_axi *axi) > +{ > + u32 value = readl(ioaddr + DMA_AXI_BUS_MODE); > + int i; > + > + pr_info("dwegmac: Master AXI performs %s burst length\n", > + !(value & DMA_AXI_UNDEF) ? "fixed" : "any"); > + > + if (axi->axi_lpi_en) > + value |= DMA_AXI_EN_LPI; > + if (axi->axi_xit_frm) > + value |= DMA_AXI_LPI_XIT_FRM; > + > + if (priv->plat->dma_cfg->dma64) { > + value &= ~DMA_AXI_WR_OSR64_LMT; > + value |= (axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR64_LMT_MASK) << > + DMA_AXI_WR_OSR64_LMT_SHIFT; > + > + value &= ~DMA_AXI_RD_OSR64_LMT; > + value |= (axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR64_LMT_MASK) << > + DMA_AXI_RD_OSR64_LMT_SHIFT; > + } else { > + value &= ~DMA_AXI_WR_OSR_LMT; > + value |= (axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR_LMT_MASK) << > + DMA_AXI_WR_OSR_LMT_SHIFT; > + > + value &= ~DMA_AXI_RD_OSR_LMT; > + value |= (axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR_LMT_MASK) << > + DMA_AXI_RD_OSR_LMT_SHIFT; > + } > + > + /* Depending on the UNDEF bit the Master AXI will perform any burst > + * length according to the BLEN programmed (by default all BLEN are > + * set). > + */ > + for (i = 0; i < AXI_BLEN; i++) { > + switch (axi->axi_blen[i]) { > + case 256: > + value |= DMA_AXI_BLEN256; > + break; > + case 128: > + value |= DMA_AXI_BLEN128; > + break; > + case 64: > + value |= DMA_AXI_BLEN64; > + break; > + case 32: > + value |= DMA_AXI_BLEN32; > + break; > + case 16: > + value |= DMA_AXI_BLEN16; > + break; > + case 8: > + value |= DMA_AXI_BLEN8; > + break; > + case 4: > + value |= DMA_AXI_BLEN4; > + break; > + } > + } > + > + writel(value, ioaddr + DMA_AXI_BUS_MODE); > +} > + > +static void dwegmac_dma_init(struct stmmac_priv *priv, void __iomem *ioaddr, > + struct stmmac_dma_cfg *dma_cfg, int atds) > +{ > + u32 value = readl(ioaddr + DMA_BUS_MODE); > + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; > + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; > + > + /* 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 (atds) > + value |= DMA_BUS_MODE_ATDS; > + > + if (dma_cfg->aal) > + value |= DMA_BUS_MODE_AAL; > + > + writel(value, ioaddr + DMA_BUS_MODE); > + > + /* Mask interrupts by writing to CSR7 */ > + writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); > +} > + > +static void dwegmac_dma_init_channel(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + struct stmmac_dma_cfg *dma_cfg, > + u32 chan) > +{ > + u32 value; > + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; > + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; > + > + if (!priv->plat->multi_msi_en) > + return; > + > + /* 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; > + > + 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, ioaddr + DMA_CHAN_INTR_ENA(chan)); > + > + if (dma_cfg->dma64) > + writel(0x100, ioaddr + DMA_CHAN_NEWFUNC_CONFIG(chan)); > +} > + > +static void dwegmac_dma_init_rx(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + struct stmmac_dma_cfg *dma_cfg, > + dma_addr_t dma_rx_phy, u32 chan) > +{ > + if (dma_cfg->dma64) { > + writel(lower_32_bits(dma_rx_phy), ioaddr + > + DMA_CHAN_RCV_BASE_ADDR64(chan)); > + writel(upper_32_bits(dma_rx_phy), ioaddr + > + DMA_CHAN_RCV_BASE_ADDR64_HI(chan)); > + writel(upper_32_bits(dma_rx_phy), ioaddr + > + DMA_RCV_BASE_ADDR64_HI_SHADOW1); > + writel(upper_32_bits(dma_rx_phy), ioaddr + > + DMA_RCV_BASE_ADDR64_HI_SHADOW2); > + } else { > + writel(lower_32_bits(dma_rx_phy), ioaddr + > + DMA_CHAN_RCV_BASE_ADDR(chan)); > + } > +} > + > +static void dwegmac_dma_init_tx(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + struct stmmac_dma_cfg *dma_cfg, > + dma_addr_t dma_tx_phy, u32 chan) > +{ > + if (dma_cfg->dma64) { > + writel(lower_32_bits(dma_tx_phy), ioaddr + > + DMA_CHAN_TX_BASE_ADDR64(chan)); > + writel(upper_32_bits(dma_tx_phy), ioaddr + > + DMA_CHAN_TX_BASE_ADDR64_HI(chan)); > + } else { > + writel(lower_32_bits(dma_tx_phy), ioaddr + > + DMA_CHAN_TX_BASE_ADDR(chan)); > + } > +} > + > +static u32 dwegmac_configure_fc(u32 csr6, int rxfifosz) > +{ > + csr6 &= ~DMA_CONTROL_RFA_MASK; > + csr6 &= ~DMA_CONTROL_RFD_MASK; > + > + /* Leave flow control disabled if receive fifo size is less than > + * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full, > + * and send XON when 2K less than full. > + */ > + if (rxfifosz < 4096) { > + csr6 &= ~DMA_CONTROL_EFC; > + pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n", > + rxfifosz); > + } else { > + csr6 |= DMA_CONTROL_EFC; > + csr6 |= RFA_FULL_MINUS_1K; > + csr6 |= RFD_FULL_MINUS_2K; > + } > + return csr6; > +} > + > +static void dwegmac_dma_operation_mode_rx(struct stmmac_priv *priv, > + void __iomem *ioaddr, int mode, > + u32 channel, int fifosz, u8 qmode) > +{ > + u32 csr6 = readl(ioaddr + DMA_CHAN_CONTROL(channel)); > + > + if (mode == SF_DMA_MODE) { > + pr_debug("GMAC: enable RX store and forward mode\n"); > + csr6 |= DMA_CONTROL_RSF; > + } else { > + pr_debug("GMAC: disable RX SF mode (threshold %d)\n", mode); > + csr6 &= ~DMA_CONTROL_RSF; > + csr6 &= DMA_CONTROL_TC_RX_MASK; > + if (mode <= 32) > + csr6 |= DMA_CONTROL_RTC_32; > + else if (mode <= 64) > + csr6 |= DMA_CONTROL_RTC_64; > + else if (mode <= 96) > + csr6 |= DMA_CONTROL_RTC_96; > + else > + csr6 |= DMA_CONTROL_RTC_128; > + } > + > + /* Configure flow control based on rx fifo size */ > + csr6 = dwegmac_configure_fc(csr6, fifosz); > + > + writel(csr6, ioaddr + DMA_CHAN_CONTROL(channel)); > +} > + > +static void dwegmac_dma_operation_mode_tx(struct stmmac_priv *priv, > + void __iomem *ioaddr, int mode, > + u32 channel, int fifosz, u8 qmode) > +{ > + u32 csr6 = readl(ioaddr + DMA_CHAN_CONTROL(channel)); > + > + if (mode == SF_DMA_MODE) { > + pr_debug("GMAC: enable TX store and forward mode\n"); > + /* Transmit COE type 2 cannot be done in cut-through mode. */ > + csr6 |= DMA_CONTROL_TSF; > + /* Operating on second frame increase the performance > + * especially when transmit store-and-forward is used. > + */ > + csr6 |= DMA_CONTROL_OSF; > + } else { > + pr_debug("GMAC: disabling TX SF (threshold %d)\n", mode); > + csr6 &= ~DMA_CONTROL_TSF; > + csr6 &= DMA_CONTROL_TC_TX_MASK; > + /* Set the transmit threshold */ > + if (mode <= 32) > + csr6 |= DMA_CONTROL_TTC_32; > + else if (mode <= 64) > + csr6 |= DMA_CONTROL_TTC_64; > + else if (mode <= 128) > + csr6 |= DMA_CONTROL_TTC_128; > + else if (mode <= 192) > + csr6 |= DMA_CONTROL_TTC_192; > + else > + csr6 |= DMA_CONTROL_TTC_256; > + } > + > + writel(csr6, ioaddr + DMA_CHAN_CONTROL(channel)); > +} > + > +static void dwegmac_dump_dma_regs(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 *reg_space) > +{ > + int i; > + > + for (i = 0; i < NUM_DWEGMAC_DMA_REGS; i++) > + if (i < 12 || i > 17) > + reg_space[DMA_BUS_MODE / 4 + i] = > + readl(ioaddr + DMA_BUS_MODE + i * 4); > +} > + > +static int dwegmac_get_hw_feature(struct stmmac_priv *priv, > + void __iomem *ioaddr, > + struct dma_features *dma_cap) > +{ > + u32 hw_cap = readl(ioaddr + DMA_HW_FEATURE); > + > + if (!hw_cap) { > + /* 0x00000000 is the value read on old hardware that does not > + * implement this register > + */ > + return -EOPNOTSUPP; > + } > + > + dma_cap->mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL); > + dma_cap->mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1; > + dma_cap->half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2; > + dma_cap->hash_filter = (hw_cap & DMA_HW_FEAT_HASHSEL) >> 4; > + dma_cap->multi_addr = (hw_cap & DMA_HW_FEAT_ADDMAC) >> 5; > + dma_cap->pcs = (hw_cap & DMA_HW_FEAT_PCSSEL) >> 6; > + dma_cap->sma_mdio = (hw_cap & DMA_HW_FEAT_SMASEL) >> 8; > + dma_cap->pmt_remote_wake_up = (hw_cap & DMA_HW_FEAT_RWKSEL) >> 9; > + dma_cap->pmt_magic_frame = (hw_cap & DMA_HW_FEAT_MGKSEL) >> 10; > + /* MMC */ > + dma_cap->rmon = (hw_cap & DMA_HW_FEAT_MMCSEL) >> 11; > + /* IEEE 1588-2002 */ > + dma_cap->time_stamp = > + (hw_cap & DMA_HW_FEAT_TSVER1SEL) >> 12; > + /* IEEE 1588-2008 */ > + dma_cap->atime_stamp = (hw_cap & DMA_HW_FEAT_TSVER2SEL) >> 13; > + /* 802.3az - Energy-Efficient Ethernet (EEE) */ > + dma_cap->eee = (hw_cap & DMA_HW_FEAT_EEESEL) >> 14; > + dma_cap->av = (hw_cap & DMA_HW_FEAT_AVSEL) >> 15; > + /* TX and RX csum */ > + dma_cap->tx_coe = (hw_cap & DMA_HW_FEAT_TXCOESEL) >> 16; > + dma_cap->rx_coe_type1 = (hw_cap & DMA_HW_FEAT_RXTYP1COE) >> 17; > + dma_cap->rx_coe_type2 = (hw_cap & DMA_HW_FEAT_RXTYP2COE) >> 18; > + dma_cap->rxfifo_over_2048 = (hw_cap & DMA_HW_FEAT_RXFIFOSIZE) >> 19; > + /* TX and RX number of channels */ > + dma_cap->number_rx_channel = (hw_cap & DMA_HW_FEAT_RXCHCNT) >> 20; > + dma_cap->number_tx_channel = (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22; > + /* Alternate (enhanced) DESC mode */ > + dma_cap->enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24; > + > + return 0; > +} > + > +static void dwegmac_rx_watchdog(struct stmmac_priv *priv, > + void __iomem *ioaddr, u32 riwt, u32 queue) > +{ > + writel(riwt, ioaddr + DMA_CHAN_RX_WATCHDOG(queue)); > +} > + > +const struct stmmac_dma_ops dwegmac_dma_ops = { > + .reset = dwegmac_dma_reset, > + .init = dwegmac_dma_init, > + .init_chan = dwegmac_dma_init_channel, > + .init_rx_chan = dwegmac_dma_init_rx, > + .init_tx_chan = dwegmac_dma_init_tx, > + .axi = dwegmac_dma_axi, > + .dump_regs = dwegmac_dump_dma_regs, > + .dma_rx_mode = dwegmac_dma_operation_mode_rx, > + .dma_tx_mode = dwegmac_dma_operation_mode_tx, > + .enable_dma_transmission = dwegmac_enable_dma_transmission, > + .enable_dma_irq = dwegmac_enable_dma_irq, > + .disable_dma_irq = dwegmac_disable_dma_irq, > + .start_tx = dwegmac_dma_start_tx, > + .stop_tx = dwegmac_dma_stop_tx, > + .start_rx = dwegmac_dma_start_rx, > + .stop_rx = dwegmac_dma_stop_rx, > + .dma_interrupt = dwegmac_dma_interrupt, > + .get_hw_feature = dwegmac_get_hw_feature, > + .rx_watchdog = dwegmac_rx_watchdog, > +}; > diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h > new file mode 100644 > index 000000000000..aadc13eae502 > --- /dev/null > +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h > @@ -0,0 +1,190 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 Loongson Technology Corporation Limited > + */ > + > +#ifndef __DWEGMAC_DMA_H__ > +#define __DWEGMAC_DMA_H__ > + > +/* DMA CRS Control and Status Register Mapping */ > +#define DMA_BUS_MODE 0x00001000 /* Bus Mode */ > +#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */ > +#define DMA_RCV_POLL_DEMAND 0x00001008 /* Received Poll Demand */ > +#define DMA_RCV_BASE_ADDR 0x0000100c /* Receive List Base */ > +#define DMA_RCV_BASE_ADDR64 0x00001090 > +#define DMA_RCV_BASE_ADDR64_HI 0x00001094 > +#define DMA_RCV_BASE_ADDR64_HI_SHADOW1 0x00001068 > +#define DMA_RCV_BASE_ADDR64_HI_SHADOW2 0x000010a8 > +#define DMA_TX_BASE_ADDR 0x00001010 /* Transmit List Base */ > +#define DMA_TX_BASE_ADDR64 0x00001098 > +#define DMA_TX_BASE_ADDR64_HI 0x0000109c > +#define DMA_STATUS 0x00001014 /* Status Register */ > +#define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ > +#define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ > +#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ > +#define DMA_NEWFUNC_CONFIG 0x00001080 /* New Function Config */ > + > +/* SW Reset */ > +#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ > + > +/* Rx watchdog register */ > +#define DMA_RX_WATCHDOG 0x00001024 > + > +/* AXI Master Bus Mode */ > +#define DMA_AXI_BUS_MODE 0x00001028 > + > +#define DMA_AXI_EN_LPI BIT(31) > +#define DMA_AXI_LPI_XIT_FRM BIT(30) > +#define DMA_AXI_WR_OSR_LMT GENMASK(23, 20) > +#define DMA_AXI_WR_OSR_LMT_SHIFT 20 > +#define DMA_AXI_WR_OSR_LMT_MASK 0xf > +#define DMA_AXI_RD_OSR_LMT GENMASK(19, 16) > +#define DMA_AXI_RD_OSR_LMT_SHIFT 16 > +#define DMA_AXI_RD_OSR_LMT_MASK 0xf > +#define DMA_AXI_WR_OSR64_LMT GENMASK(21, 20) > +#define DMA_AXI_WR_OSR64_LMT_SHIFT 20 > +#define DMA_AXI_WR_OSR64_LMT_MASK 0x3 > +#define DMA_AXI_RD_OSR64_LMT GENMASK(17, 16) > +#define DMA_AXI_RD_OSR64_LMT_SHIFT 16 > +#define DMA_AXI_RD_OSR64_LMT_MASK 0x3 > + > +#define DMA_AXI_OSR_MAX 0xf > +#define DMA_AXI_MAX_OSR_LIMIT ((DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT) | \ > + (DMA_AXI_OSR_MAX << DMA_AXI_RD_OSR_LMT_SHIFT)) > +#define DMA_AXI_OSR64_MAX 0x3 > +#define DMA_AXI_MAX_OSR64_LIMIT ((DMA_AXI_OSR64_MAX << DMA_AXI_WR_OSR64_LMT_SHIFT) | \ > + (DMA_AXI_OSR64_MAX << DMA_AXI_RD_OSR64_LMT_SHIFT)) > +#define DMA_AXI_1KBBE BIT(13) > +#define DMA_AXI_AAL BIT(12) > +#define DMA_AXI_BLEN256 BIT(7) > +#define DMA_AXI_BLEN128 BIT(6) > +#define DMA_AXI_BLEN64 BIT(5) > +#define DMA_AXI_BLEN32 BIT(4) > +#define DMA_AXI_BLEN16 BIT(3) > +#define DMA_AXI_BLEN8 BIT(2) > +#define DMA_AXI_BLEN4 BIT(1) > +#define DMA_BURST_LEN_DEFAULT (DMA_AXI_BLEN256 | DMA_AXI_BLEN128 | \ > + DMA_AXI_BLEN64 | DMA_AXI_BLEN32 | \ > + DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | \ > + DMA_AXI_BLEN4) > + > +#define DMA_AXI_UNDEF BIT(0) > + > +#define DMA_AXI_BURST_LEN_MASK 0x000000fe > + > +#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */ > +#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */ > +#define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */ > + > +/* DMA Control register defines */ > +#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ > +#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ > + > +/* DMA Normal interrupt */ > +#define DMA_INTR_ENA_NIE 0x00060000 /* Normal Summary */ > +#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */ > +#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */ > +#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */ > +#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */ > + > +#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \ > + DMA_INTR_ENA_TIE) > + > +/* DMA Abnormal interrupt */ > +#define DMA_INTR_ENA_AIE 0x00018000 /* Abnormal Summary */ > +#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */ > +#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */ > +#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */ > +#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */ > +#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */ > +#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */ > +#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */ > +#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */ > +#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */ > + > +#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \ > + DMA_INTR_ENA_UNE) > + > +/* DMA default interrupt mask */ > +#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) > +#define DMA_INTR_DEFAULT_RX (DMA_INTR_ENA_RIE) > +#define DMA_INTR_DEFAULT_TX (DMA_INTR_ENA_TIE) > + > +/* DMA Status register defines */ > +#define DMA_STATUS_GLPII 0x10000000 /* GMAC LPI interrupt */ > +#define DMA_STATUS_EB_MASK 0x0e000000 /* Error Bits Mask */ > +#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ > +#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ > +#define DMA_STATUS_TS_MASK 0x01c00000 /* Transmit Process State */ > +#define DMA_STATUS_TS_SHIFT 22 > +#define DMA_STATUS_RS_MASK 0x00380000 /* Receive Process State */ > +#define DMA_STATUS_RS_SHIFT 19 > +#define DMA_STATUS_TX_NIS 0x00040000 /* Normal Tx Interrupt Summary */ > +#define DMA_STATUS_RX_NIS 0x00020000 /* Normal Rx Interrupt Summary */ > +#define DMA_STATUS_TX_AIS 0x00010000 /* Abnormal Tx Interrupt Summary */ > +#define DMA_STATUS_RX_AIS 0x00008000 /* Abnormal Rx Interrupt Summary */ > +#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ > +#define DMA_STATUS_TX_FBI 0x00002000 /* Fatal Tx Bus Error Interrupt */ > +#define DMA_STATUS_RX_FBI 0x00001000 /* Fatal Rx Bus Error Interrupt */ > +#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ > +#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ > +#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ > +#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ > +#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ > +#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ > +#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ > +#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ > +#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ > +#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ > +#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ > +#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ > + > +#define DMA_STATUS_MSK_RX_COMMON (DMA_STATUS_RX_NIS | \ > + DMA_STATUS_RX_AIS | \ > + DMA_STATUS_RX_FBI) > + > +#define DMA_STATUS_MSK_TX_COMMON (DMA_STATUS_TX_NIS | \ > + DMA_STATUS_TX_AIS | \ > + DMA_STATUS_TX_FBI) > + > +#define DMA_STATUS_MSK_RX (DMA_STATUS_ERI | \ > + DMA_STATUS_RWT | \ > + DMA_STATUS_RPS | \ > + DMA_STATUS_RU | \ > + DMA_STATUS_RI | \ > + DMA_STATUS_OVF | \ > + DMA_STATUS_MSK_RX_COMMON) > + > +#define DMA_STATUS_MSK_TX (DMA_STATUS_ETI | \ > + DMA_STATUS_UNF | \ > + DMA_STATUS_TJT | \ > + DMA_STATUS_TU | \ > + DMA_STATUS_TPS | \ > + DMA_STATUS_TI | \ > + DMA_STATUS_MSK_TX_COMMON) > + > +/* Following DMA defines are chanels oriented */ > +#define DMA_CHAN_OFFSET 0x100 > + > +static inline u32 dma_chan_base_addr(u32 base, u32 chan) > +{ > + return base + chan * DMA_CHAN_OFFSET; > +} > + > +#define DMA_CHAN_NEWFUNC_CONFIG(chan) dma_chan_base_addr(DMA_NEWFUNC_CONFIG, chan) > +#define DMA_CHAN_XMT_POLL_DEMAND(chan) dma_chan_base_addr(DMA_XMT_POLL_DEMAND, chan) > +#define DMA_CHAN_INTR_ENA(chan) dma_chan_base_addr(DMA_INTR_ENA, chan) > +#define DMA_CHAN_CONTROL(chan) dma_chan_base_addr(DMA_CONTROL, chan) > +#define DMA_CHAN_STATUS(chan) dma_chan_base_addr(DMA_STATUS, chan) > +#define DMA_CHAN_BUS_MODE(chan) dma_chan_base_addr(DMA_BUS_MODE, chan) > +#define DMA_CHAN_RCV_BASE_ADDR(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR, chan) > +#define DMA_CHAN_RCV_BASE_ADDR64(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR64, chan) > +#define DMA_CHAN_RCV_BASE_ADDR64_HI(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR64_HI, chan) > +#define DMA_CHAN_TX_BASE_ADDR(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR, chan) > +#define DMA_CHAN_TX_BASE_ADDR64(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR64, chan) > +#define DMA_CHAN_TX_BASE_ADDR64_HI(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR64_HI, chan) > +#define DMA_CHAN_RX_WATCHDOG(chan) dma_chan_base_addr(DMA_RX_WATCHDOG, chan) > + > +#define NUM_DWEGMAC_DMA_REGS 23 > + > +#endif /* __DWEGMAC_DMA_H__ */ > diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c > index c5768bbec38e..4674af3d78cb 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/hwif.c > +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c > @@ -61,7 +61,7 @@ static int stmmac_dwmac1_quirks(struct stmmac_priv *priv) > dev_info(priv->device, "Enhanced/Alternate descriptors\n"); > > /* GMAC older than 3.50 has no extended descriptors */ > - if (priv->synopsys_id >= DWMAC_CORE_3_50) { > + if (priv->synopsys_id >= DWMAC_CORE_3_50 || priv->plat->has_egmac) { > dev_info(priv->device, "Enabled extended descriptors\n"); > priv->extend_desc = 1; > } else { > @@ -107,6 +107,7 @@ static const struct stmmac_hwif_entry { > bool gmac; > bool gmac4; > bool xgmac; > + bool egmac; > u32 min_id; > u32 dev_id; > const struct stmmac_regs_off regs; > @@ -125,6 +126,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = false, > .xgmac = false, > + .egmac = false, > .min_id = 0, > .regs = { > .ptp_off = PTP_GMAC3_X_OFFSET, > @@ -143,6 +145,7 @@ static const struct stmmac_hwif_entry { > .gmac = true, > .gmac4 = false, > .xgmac = false, > + .egmac = false, > .min_id = 0, > .regs = { > .ptp_off = PTP_GMAC3_X_OFFSET, > @@ -161,6 +164,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = true, > .xgmac = false, > + .egmac = false, > .min_id = 0, > .regs = { > .ptp_off = PTP_GMAC4_OFFSET, > @@ -179,6 +183,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = true, > .xgmac = false, > + .egmac = false, > .min_id = DWMAC_CORE_4_00, > .regs = { > .ptp_off = PTP_GMAC4_OFFSET, > @@ -197,6 +202,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = true, > .xgmac = false, > + .egmac = false, > .min_id = DWMAC_CORE_4_10, > .regs = { > .ptp_off = PTP_GMAC4_OFFSET, > @@ -215,6 +221,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = true, > .xgmac = false, > + .egmac = false, > .min_id = DWMAC_CORE_5_10, > .regs = { > .ptp_off = PTP_GMAC4_OFFSET, > @@ -233,6 +240,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = false, > .xgmac = true, > + .egmac = false, > .min_id = DWXGMAC_CORE_2_10, > .dev_id = DWXGMAC_ID, > .regs = { > @@ -252,6 +260,7 @@ static const struct stmmac_hwif_entry { > .gmac = false, > .gmac4 = false, > .xgmac = true, > + .egmac = false, > .min_id = DWXLGMAC_CORE_2_00, > .dev_id = DWXLGMAC_ID, > .regs = { > @@ -267,11 +276,50 @@ static const struct stmmac_hwif_entry { > .mmc = &dwxgmac_mmc_ops, > .setup = dwxlgmac2_setup, > .quirks = stmmac_dwxlgmac_quirks, > + }, { > + .gmac = false, > + .gmac4 = false, > + .xgmac = false, > + .egmac = true, > + .min_id = DWEGMAC_CORE_1_00, > + .regs = { > + .ptp_off = PTP_GMAC3_X_OFFSET, > + .mmc_off = MMC_GMAC3_X_OFFSET, > + }, > + .desc = NULL, > + .dma = &dwegmac_dma_ops, > + .mac = &dwegmac_ops, > + .hwtimestamp = &stmmac_ptp, > + .mode = NULL, > + .tc = NULL, > + .mmc = &dwmac_mmc_ops, > + .setup = dwegmac_setup, > + .quirks = stmmac_dwmac1_quirks, > + }, { > + .gmac = false, > + .gmac4 = false, > + .xgmac = false, > + .egmac = true, > + .min_id = DWMAC_CORE_3_50, > + .regs = { > + .ptp_off = PTP_GMAC3_X_OFFSET, > + .mmc_off = MMC_GMAC3_X_OFFSET, > + }, > + .desc = NULL, > + .dma = &dwmac1000_dma_ops, > + .mac = &dwmac1000_ops, > + .hwtimestamp = &stmmac_ptp, > + .mode = NULL, > + .tc = NULL, > + .mmc = &dwmac_mmc_ops, > + .setup = dwmac1000_setup, > + .quirks = stmmac_dwmac1_quirks, > }, > }; > > int stmmac_hwif_init(struct stmmac_priv *priv) > { > + bool needs_egmac = priv->plat->has_egmac; > bool needs_xgmac = priv->plat->has_xgmac; > bool needs_gmac4 = priv->plat->has_gmac4; > bool needs_gmac = priv->plat->has_gmac; > @@ -281,7 +329,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv) > u32 id, dev_id = 0; > int i, ret; > > - if (needs_gmac) { > + if (needs_gmac || needs_egmac) { > id = stmmac_get_id(priv, GMAC_VERSION); > } else if (needs_gmac4 || needs_xgmac) { > id = stmmac_get_id(priv, GMAC4_VERSION); > @@ -321,6 +369,8 @@ int stmmac_hwif_init(struct stmmac_priv *priv) > continue; > if (needs_xgmac ^ entry->xgmac) > continue; > + if (needs_egmac ^ entry->egmac) > + continue; > /* Use synopsys_id var because some setups can override this */ > if (priv->synopsys_id < entry->min_id) > continue; > diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h > index 00be9a7003c8..41989903e8c9 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h > +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h > @@ -667,6 +667,8 @@ extern const struct stmmac_dma_ops dwxgmac210_dma_ops; > extern const struct stmmac_desc_ops dwxgmac210_desc_ops; > extern const struct stmmac_mmc_ops dwmac_mmc_ops; > extern const struct stmmac_mmc_ops dwxgmac_mmc_ops; > +extern const struct stmmac_ops dwegmac_ops; > +extern const struct stmmac_dma_ops dwegmac_dma_ops; > > #define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ > #define GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */ > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > index 2ae73ab842d4..dba33ce392a8 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c > @@ -596,7 +596,7 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, > priv->xstats.phy_eee_wakeup_error_n = val; > } > > - if (priv->synopsys_id >= DWMAC_CORE_3_50) > + if (priv->synopsys_id >= DWMAC_CORE_3_50 || priv->plat->has_egmac) > stmmac_mac_debug(priv, priv->ioaddr, > (void *)&priv->xstats, > rx_queues_count, tx_queues_count); > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c > index e8619853b6d6..f91dd3f69fef 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c > @@ -6936,7 +6936,8 @@ static int stmmac_hw_init(struct stmmac_priv *priv) > * riwt_off field from the platform. > */ > if (((priv->synopsys_id >= DWMAC_CORE_3_50) || > - (priv->plat->has_xgmac)) && (!priv->plat->riwt_off)) { > + (priv->plat->has_xgmac) || (priv->plat->has_egmac)) && > + (!priv->plat->riwt_off)) { > priv->use_riwt = 1; > dev_info(priv->device, > "Enable RX Mitigation via HW Watchdog Timer\n"); > diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h > index 2fcd83f6db14..0e36259a9568 100644 > --- a/include/linux/stmmac.h > +++ b/include/linux/stmmac.h > @@ -295,5 +295,6 @@ struct plat_stmmacenet_data { > bool serdes_up_after_phy_linkup; > const struct dwmac4_addrs *dwmac4_addrs; > bool has_integrated_pcs; > + int has_egmac; > }; > #endif > -- > 2.39.3 > >
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 10f32ded2bd9..1238cd736910 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \ dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \ stmmac_tc.o dwxgmac2_core.o dwxgmac2_dma.o dwxgmac2_descs.o \ - stmmac_xdp.o ring_mode64.o \ + stmmac_xdp.o ring_mode64.o dwegmac_core.o dwegmac_dma.o \ $(stmmac-y) stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 90a7784f71cb..74528a16b93a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -36,6 +36,7 @@ #define DWMAC_CORE_5_20 0x52 #define DWXGMAC_CORE_2_10 0x21 #define DWXLGMAC_CORE_2_00 0x20 +#define DWEGMAC_CORE_1_00 0x10 /* Device ID */ #define DWXGMAC_ID 0x76 @@ -547,6 +548,7 @@ int dwmac1000_setup(struct stmmac_priv *priv); int dwmac4_setup(struct stmmac_priv *priv); int dwxgmac2_setup(struct stmmac_priv *priv); int dwxlgmac2_setup(struct stmmac_priv *priv); +int dwegmac_setup(struct stmmac_priv *priv); void stmmac_set_mac_addr(void __iomem *ioaddr, const u8 addr[6], unsigned int high, unsigned int low); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac.h b/drivers/net/ethernet/stmicro/stmmac/dwegmac.h new file mode 100644 index 000000000000..6f8fcf3ed409 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac.h @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __DWEGMAC_H__ +#define __DWEGMAC_H__ + +#include <linux/phy.h> +#include "common.h" + +#define GMAC_CONTROL 0x00000000 /* Configuration */ +#define GMAC_FRAME_FILTER 0x00000004 /* Frame Filter */ +#define GMAC_HASH_HIGH 0x00000008 /* Multicast Hash Table High */ +#define GMAC_HASH_LOW 0x0000000c /* Multicast Hash Table Low */ +#define GMAC_MII_ADDR 0x00000010 /* MII Address */ +#define GMAC_MII_DATA 0x00000014 /* MII Data */ +#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */ +#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */ +#define GMAC_DEBUG 0x00000024 /* GMAC debug register */ +#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ + +#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ +#define GMAC_INT_STATUS_PMT BIT(3) +#define GMAC_INT_STATUS_MMCIS BIT(4) +#define GMAC_INT_STATUS_MMCRIS BIT(5) +#define GMAC_INT_STATUS_MMCTIS BIT(6) +#define GMAC_INT_STATUS_MMCCSUM BIT(7) +#define GMAC_INT_STATUS_TSTAMP BIT(9) +#define GMAC_INT_STATUS_LPIIS BIT(10) + +/* interrupt mask register */ +#define GMAC_INT_MASK 0x0000003c +#define GMAC_INT_DISABLE_RGMII BIT(0) +#define GMAC_INT_DISABLE_PCSLINK BIT(1) +#define GMAC_INT_DISABLE_PCSAN BIT(2) +#define GMAC_INT_DISABLE_PMT BIT(3) +#define GMAC_INT_DISABLE_TIMESTAMP BIT(9) +#define GMAC_INT_DISABLE_PCS (GMAC_INT_DISABLE_RGMII | \ + GMAC_INT_DISABLE_PCSLINK | \ + GMAC_INT_DISABLE_PCSAN) +#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_TIMESTAMP | \ + GMAC_INT_DISABLE_PCS) + +/* PMT Control and Status */ +#define GMAC_PMT 0x0000002c +enum power_event { + pointer_reset = 0x80000000, + global_unicast = 0x00000200, + wake_up_rx_frame = 0x00000040, + magic_frame = 0x00000020, + wake_up_frame_en = 0x00000004, + magic_pkt_en = 0x00000002, + power_down = 0x00000001, +}; + +/* Energy Efficient Ethernet (EEE) + * + * LPI status, timer and control register offset + */ +#define LPI_CTRL_STATUS 0x0030 +#define LPI_TIMER_CTRL 0x0034 + +/* LPI control and status defines */ +#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */ +#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */ +#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */ +#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */ +#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */ +#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */ +#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */ +#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */ +#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */ +#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */ + +/* GMAC HW ADDR regs */ +#define GMAC_ADDR_HIGH(reg) ((reg > 15) ? 0x00000800 + (reg - 16) * 8 : \ + 0x00000040 + (reg * 8)) +#define GMAC_ADDR_LOW(reg) ((reg > 15) ? 0x00000804 + (reg - 16) * 8 : \ + 0x00000044 + (reg * 8)) +#define GMAC_MAX_PERFECT_ADDRESSES 1 + +#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */ +#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */ + +/* SGMII/RGMII status register */ +#define GMAC_RGSMIIIS_LNKMODE BIT(0) +#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1) +#define GMAC_RGSMIIIS_SPEED_SHIFT 1 +#define GMAC_RGSMIIIS_LNKSTS BIT(3) +#define GMAC_RGSMIIIS_JABTO BIT(4) +#define GMAC_RGSMIIIS_FALSECARDET BIT(5) +#define GMAC_RGSMIIIS_SMIDRXS BIT(16) +/* LNKMOD */ +#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1 +/* LNKSPEED */ +#define GMAC_RGSMIIIS_SPEED_125 0x2 +#define GMAC_RGSMIIIS_SPEED_25 0x1 +#define GMAC_RGSMIIIS_SPEED_2_5 0x0 + +/* GMAC Configuration defines */ +#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */ +#define GMAC_CONTROL_TC 0x01000000 /* Transmit Conf. in RGMII/SGMII */ +#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */ +#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */ +#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */ +#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ +enum inter_frame_gap { + GMAC_CONTROL_IFG_88 = 0x00040000, + GMAC_CONTROL_IFG_80 = 0x00020000, + GMAC_CONTROL_IFG_40 = 0x000e0000, +}; +#define GMAC_CONTROL_DCRS 0x00010000 /* Disable carrier sense */ +#define GMAC_CONTROL_PS 0x00008000 /* Port Select 0:GMI 1:MII */ +#define GMAC_CONTROL_FES 0x00004000 /* Speed 0:10 1:100 */ +#define GMAC_CONTROL_DO 0x00002000 /* Disable Rx Own */ +#define GMAC_CONTROL_LM 0x00001000 /* Loop-back mode */ +#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */ +#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */ +#define GMAC_CONTROL_DR 0x00000200 /* Disable Retry */ +#define GMAC_CONTROL_LUD 0x00000100 /* Link up/down */ +#define GMAC_CONTROL_ACS 0x00000080 /* Auto Pad/FCS Stripping */ +#define GMAC_CONTROL_DC 0x00000010 /* Deferral Check */ +#define GMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ +#define GMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ + +#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | \ + GMAC_CONTROL_BE | GMAC_CONTROL_DCRS | \ + GMAC_CONTROL_ACS) + +/* GMAC Frame Filter defines */ +#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */ +#define GMAC_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */ +#define GMAC_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */ +#define GMAC_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */ +#define GMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */ +#define GMAC_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */ +#define GMAC_FRAME_FILTER_PCF 0x00000080 /* Pass Control frames */ +#define GMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ +#define GMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ +#define GMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ +#define GMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ +/* GMII ADDR defines */ +#define GMAC_MII_ADDR_WRITE 0x00000002 /* MII Write */ +#define GMAC_MII_ADDR_BUSY 0x00000001 /* MII Busy */ +/* GMAC FLOW CTRL defines */ +#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ +#define GMAC_FLOW_CTRL_PT_SHIFT 16 +#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */ +#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ +#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ +#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ + +/* DEBUG Register defines */ +/* MTL TxStatus FIFO */ +#define GMAC_DEBUG_TXSTSFSTS BIT(25) /* MTL TxStatus FIFO Full Status */ +#define GMAC_DEBUG_TXFSTS BIT(24) /* MTL Tx FIFO Not Empty Status */ +#define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */ +/* MTL Tx FIFO Read Controller Status */ +#define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20) +#define GMAC_DEBUG_TRCSTS_SHIFT 20 +#define GMAC_DEBUG_TRCSTS_IDLE 0 +#define GMAC_DEBUG_TRCSTS_READ 1 +#define GMAC_DEBUG_TRCSTS_TXW 2 +#define GMAC_DEBUG_TRCSTS_WRITE 3 +#define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */ +/* MAC Transmit Frame Controller Status */ +#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17) +#define GMAC_DEBUG_TFCSTS_SHIFT 17 +#define GMAC_DEBUG_TFCSTS_IDLE 0 +#define GMAC_DEBUG_TFCSTS_WAIT 1 +#define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2 +#define GMAC_DEBUG_TFCSTS_XFER 3 +/* MAC GMII or MII Transmit Protocol Engine Status */ +#define GMAC_DEBUG_TPESTS BIT(16) +#define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */ +#define GMAC_DEBUG_RXFSTS_SHIFT 8 +#define GMAC_DEBUG_RXFSTS_EMPTY 0 +#define GMAC_DEBUG_RXFSTS_BT 1 +#define GMAC_DEBUG_RXFSTS_AT 2 +#define GMAC_DEBUG_RXFSTS_FULL 3 +#define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */ +#define GMAC_DEBUG_RRCSTS_SHIFT 5 +#define GMAC_DEBUG_RRCSTS_IDLE 0 +#define GMAC_DEBUG_RRCSTS_RDATA 1 +#define GMAC_DEBUG_RRCSTS_RSTAT 2 +#define GMAC_DEBUG_RRCSTS_FLUSH 3 +#define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */ +/* MAC Receive Frame Controller FIFO Status */ +#define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1) +#define GMAC_DEBUG_RFCFCSTS_SHIFT 1 +/* MAC GMII or MII Receive Protocol Engine Status */ +#define GMAC_DEBUG_RPESTS BIT(0) + +/*--- DMA BLOCK defines ---*/ +/* DMA Bus Mode register defines */ +#define DMA_BUS_MODE_DA 0x00000002 /* Arbitration scheme */ +#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ +#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ +/* Programmable burst length (passed thorugh platform)*/ +#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ +#define DMA_BUS_MODE_PBL_SHIFT 8 +#define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */ + +enum rx_tx_priority_ratio { + double_ratio = 0x00004000, /* 2:1 */ + triple_ratio = 0x00008000, /* 3:1 */ + quadruple_ratio = 0x0000c000, /* 4:1 */ +}; + +#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ +#define DMA_BUS_MODE_MB 0x04000000 /* Mixed burst */ +#define DMA_BUS_MODE_RPBL_MASK 0x007e0000 /* Rx-Programmable Burst Len */ +#define DMA_BUS_MODE_RPBL_SHIFT 17 +#define DMA_BUS_MODE_USP 0x00800000 +#define DMA_BUS_MODE_MAXPBL 0x01000000 +#define DMA_BUS_MODE_AAL 0x02000000 + +/* DMA CRS Control and Status Register Mapping */ +#define DMA_HOST_TX_DESC 0x00001048 /* Current Host Tx descriptor */ +#define DMA_HOST_RX_DESC 0x0000104c /* Current Host Rx descriptor */ +/* DMA Bus Mode register defines */ +#define DMA_BUS_PR_RATIO_MASK 0x0000c000 /* Rx/Tx priority ratio */ +#define DMA_BUS_PR_RATIO_SHIFT 14 +#define DMA_BUS_FB 0x00010000 /* Fixed Burst */ + +/* DMA operation mode defines (start/stop tx/rx are placed in common header)*/ +/* Disable Drop TCP/IP csum error */ +#define DMA_CONTROL_DT 0x04000000 +#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */ +#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */ +/* Threshold for Activating the FC */ +enum rfa { + act_full_minus_1 = 0x00800000, + act_full_minus_2 = 0x00800200, + act_full_minus_3 = 0x00800400, + act_full_minus_4 = 0x00800600, +}; +/* Threshold for Deactivating the FC */ +enum rfd { + deac_full_minus_1 = 0x00400000, + deac_full_minus_2 = 0x00400800, + deac_full_minus_3 = 0x00401000, + deac_full_minus_4 = 0x00401800, +}; +#define DMA_CONTROL_TSF 0x00200000 /* Transmit Store and Forward */ + +enum ttc_control { + DMA_CONTROL_TTC_64 = 0x00000000, + DMA_CONTROL_TTC_128 = 0x00004000, + DMA_CONTROL_TTC_192 = 0x00008000, + DMA_CONTROL_TTC_256 = 0x0000c000, + DMA_CONTROL_TTC_40 = 0x00010000, + DMA_CONTROL_TTC_32 = 0x00014000, + DMA_CONTROL_TTC_24 = 0x00018000, + DMA_CONTROL_TTC_16 = 0x0001c000, +}; +#define DMA_CONTROL_TC_TX_MASK 0xfffe3fff + +#define DMA_CONTROL_EFC 0x00000100 +#define DMA_CONTROL_FEF 0x00000080 +#define DMA_CONTROL_FUF 0x00000040 + +/* Receive flow control activation field + * RFA field in DMA control register, bits 23,10:9 + */ +#define DMA_CONTROL_RFA_MASK 0x00800600 + +/* Receive flow control deactivation field + * RFD field in DMA control register, bits 22,12:11 + */ +#define DMA_CONTROL_RFD_MASK 0x00401800 + +/* RFD and RFA fields are encoded as follows + * + * Bit Field + * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled) + * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled) + * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled) + * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled) + * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled) + * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled) + * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled) + * 1,11 - Reserved + * + * RFD should always be > RFA for a given FIFO size. RFD == RFA may work, + * but packet throughput performance may not be as expected. + * + * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame + * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause + * Description). + * + * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6), + * is set to 0. This allows pause frames with a quanta of 0 to be sent + * as an XOFF message to the link peer. + */ + +#define RFA_FULL_MINUS_1K 0x00000000 +#define RFA_FULL_MINUS_2K 0x00000200 +#define RFA_FULL_MINUS_3K 0x00000400 +#define RFA_FULL_MINUS_4K 0x00000600 +#define RFA_FULL_MINUS_5K 0x00800000 +#define RFA_FULL_MINUS_6K 0x00800200 +#define RFA_FULL_MINUS_7K 0x00800400 + +#define RFD_FULL_MINUS_1K 0x00000000 +#define RFD_FULL_MINUS_2K 0x00000800 +#define RFD_FULL_MINUS_3K 0x00001000 +#define RFD_FULL_MINUS_4K 0x00001800 +#define RFD_FULL_MINUS_5K 0x00400000 +#define RFD_FULL_MINUS_6K 0x00400800 +#define RFD_FULL_MINUS_7K 0x00401000 + +enum rtc_control { + DMA_CONTROL_RTC_64 = 0x00000000, + DMA_CONTROL_RTC_32 = 0x00000008, + DMA_CONTROL_RTC_96 = 0x00000010, + DMA_CONTROL_RTC_128 = 0x00000018, +}; +#define DMA_CONTROL_TC_RX_MASK 0xffffffe7 + +#define DMA_CONTROL_OSF 0x00000004 /* Operate on second frame */ + +/* MMC registers offset */ +#define GMAC_MMC_CTRL 0x100 +#define GMAC_MMC_RX_INTR 0x104 +#define GMAC_MMC_TX_INTR 0x108 +#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 +#define GMAC_EXTHASH_BASE 0x500 + +extern const struct stmmac_dma_ops dwegmac_dma_ops; +#endif /* __DWEGMAC_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c b/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c new file mode 100644 index 000000000000..4e0c8731adfb --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_core.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/crc32.h> +#include <linux/slab.h> +#include <linux/ethtool.h> +#include <linux/io.h> +#include "stmmac.h" +#include "stmmac_pcs.h" +#include "dwegmac.h" + +static void dwegmac_core_init(struct mac_device_info *hw, + struct net_device *dev) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value = readl(ioaddr + GMAC_CONTROL); + int mtu = dev->mtu; + + /* Configure GMAC core */ + value |= GMAC_CORE_INIT; + + if (mtu > 1500) + value |= GMAC_CONTROL_2K; + if (mtu > 2000) + value |= GMAC_CONTROL_JE; + + if (hw->ps) { + value |= GMAC_CONTROL_TE; + + value &= ~hw->link.speed_mask; + switch (hw->ps) { + case SPEED_1000: + value |= hw->link.speed1000; + break; + case SPEED_100: + value |= hw->link.speed100; + break; + case SPEED_10: + value |= hw->link.speed10; + break; + } + } + + writel(value, ioaddr + GMAC_CONTROL); + + /* Mask GMAC interrupts */ + value = GMAC_INT_DEFAULT_MASK; + + if (hw->pcs) + value &= ~GMAC_INT_DISABLE_PCS; + + writel(value, ioaddr + GMAC_INT_MASK); + +#ifdef STMMAC_VLAN_TAG_USED + /* Tag detection without filtering */ + writel(0x0, ioaddr + GMAC_VLAN_TAG); +#endif +} + +static int dwegmac_rx_ipc_enable(struct mac_device_info *hw) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value = readl(ioaddr + GMAC_CONTROL); + + if (hw->rx_csum) + value |= GMAC_CONTROL_IPC; + else + value &= ~GMAC_CONTROL_IPC; + + writel(value, ioaddr + GMAC_CONTROL); + + value = readl(ioaddr + GMAC_CONTROL); + + return !!(value & GMAC_CONTROL_IPC); +} + +static void dwegmac_dump_regs(struct mac_device_info *hw, u32 *reg_space) +{ + void __iomem *ioaddr = hw->pcsr; + int i; + + for (i = 0; i < 55; i++) + reg_space[i] = readl(ioaddr + i * 4); +} + +static void dwegmac_set_umac_addr(struct stmmac_priv *priv, + struct mac_device_info *hw, + const unsigned char *addr, + unsigned int reg_n) +{ + void __iomem *ioaddr = hw->pcsr; + + stmmac_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), + GMAC_ADDR_LOW(reg_n)); +} + +static void dwegmac_get_umac_addr(struct stmmac_priv *priv, + struct mac_device_info *hw, + unsigned char *addr, + unsigned int reg_n) +{ + void __iomem *ioaddr = hw->pcsr; + + stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n), + GMAC_ADDR_LOW(reg_n)); +} + +static void dwegmac_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits, + int mcbitslog2) +{ + int numhashregs, regs; + + switch (mcbitslog2) { + case 6: + writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW); + writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH); + return; + case 7: + numhashregs = 4; + break; + case 8: + numhashregs = 8; + break; + default: + pr_debug("STMMAC: err in setting multicast filter\n"); + return; + } + for (regs = 0; regs < numhashregs; regs++) + writel(mcfilterbits[regs], + ioaddr + GMAC_EXTHASH_BASE + regs * 4); +} + +static void dwegmac_set_filter(struct stmmac_priv *priv, + struct mac_device_info *hw, + struct net_device *dev) +{ + void __iomem *ioaddr = (void __iomem *)dev->base_addr; + unsigned int value = 0; + unsigned int perfect_addr_number = hw->unicast_filter_entries; + u32 mc_filter[8]; + int mcbitslog2 = hw->mcast_bits_log2; + + pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, + netdev_mc_count(dev), netdev_uc_count(dev)); + + memset(mc_filter, 0, sizeof(mc_filter)); + + if (dev->flags & IFF_PROMISC) { + value = GMAC_FRAME_FILTER_PR | GMAC_FRAME_FILTER_PCF; + } else if (dev->flags & IFF_ALLMULTI) { + value = GMAC_FRAME_FILTER_PM; /* pass all multi */ + } else if (!netdev_mc_empty(dev) && (mcbitslog2 == 0)) { + /* Fall back to all multicast if we've no filter */ + value = GMAC_FRAME_FILTER_PM; + } else if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + + /* Hash filter for multicast */ + value = GMAC_FRAME_FILTER_HMC; + + netdev_for_each_mc_addr(ha, dev) { + /* The upper n bits of the calculated CRC are used to + * index the contents of the hash table. The number of + * bits used depends on the hardware configuration + * selected at core configuration time. + */ + int bit_nr = bitrev32(~crc32_le(~0, ha->addr, + ETH_ALEN)) >> + (32 - mcbitslog2); + /* The most significant bit determines the register to + * use (H/L) while the other 5 bits determine the bit + * within the register. + */ + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + } + } + + value |= GMAC_FRAME_FILTER_HPF; + dwegmac_set_mchash(ioaddr, mc_filter, mcbitslog2); + + /* Handle multiple unicast addresses (perfect filtering) */ + if (netdev_uc_count(dev) > perfect_addr_number) { + /* Switch to promiscuous mode if more than unicast + * addresses are requested than supported by hardware. + */ + value |= GMAC_FRAME_FILTER_PR; + } else { + int reg = 1; + struct netdev_hw_addr *ha; + + netdev_for_each_uc_addr(ha, dev) { + stmmac_set_mac_addr(ioaddr, ha->addr, + GMAC_ADDR_HIGH(reg), + GMAC_ADDR_LOW(reg)); + reg++; + } + + while (reg < perfect_addr_number) { + writel(0, ioaddr + GMAC_ADDR_HIGH(reg)); + writel(0, ioaddr + GMAC_ADDR_LOW(reg)); + reg++; + } + } + +#ifdef FRAME_FILTER_DEBUG + /* Enable Receive all mode (to debug filtering_fail errors) */ + value |= GMAC_FRAME_FILTER_RA; +#endif + writel(value, ioaddr + GMAC_FRAME_FILTER); +} + +static void dwegmac_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, + unsigned int fc, unsigned int pause_time, + u32 tx_cnt) +{ + void __iomem *ioaddr = hw->pcsr; + /* Set flow such that DZPQ in Mac Register 6 is 0, + * and unicast pause detect is enabled. + */ + unsigned int flow = GMAC_FLOW_CTRL_UP; + + pr_debug("GMAC Flow-Control:\n"); + if (fc & FLOW_RX) { + pr_debug("\tReceive Flow-Control ON\n"); + flow |= GMAC_FLOW_CTRL_RFE; + } + if (fc & FLOW_TX) { + pr_debug("\tTransmit Flow-Control ON\n"); + flow |= GMAC_FLOW_CTRL_TFE; + } + + if (duplex) { + pr_debug("\tduplex mode: PAUSE %d\n", pause_time); + flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); + } + + writel(flow, ioaddr + GMAC_FLOW_CTRL); +} + +static void dwegmac_pmt(struct mac_device_info *hw, unsigned long mode) +{ + void __iomem *ioaddr = hw->pcsr; + unsigned int pmt = 0; + + if (mode & WAKE_MAGIC) { + pr_debug("GMAC: WOL Magic frame\n"); + pmt |= power_down | magic_pkt_en; + } + if (mode & WAKE_UCAST) { + pr_debug("GMAC: WOL on global unicast\n"); + pmt |= power_down | global_unicast | wake_up_frame_en; + } + + writel(pmt, ioaddr + GMAC_PMT); +} + +/* RGMII or SMII interface */ +static void dwegmac_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x) +{ + u32 status; + + status = readl(ioaddr + GMAC_RGSMIIIS); + x->irq_rgmii_n++; + + /* Check the link status */ + if (status & GMAC_RGSMIIIS_LNKSTS) { + int speed_value; + + x->pcs_link = 1; + + speed_value = ((status & GMAC_RGSMIIIS_SPEED) >> + GMAC_RGSMIIIS_SPEED_SHIFT); + if (speed_value == GMAC_RGSMIIIS_SPEED_125) + x->pcs_speed = SPEED_1000; + else if (speed_value == GMAC_RGSMIIIS_SPEED_25) + x->pcs_speed = SPEED_100; + else + x->pcs_speed = SPEED_10; + + x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK); + + pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, + x->pcs_duplex ? "Full" : "Half"); + } else { + x->pcs_link = 0; + pr_info("Link is Down\n"); + } +} + +static int dwegmac_irq_status(struct mac_device_info *hw, + struct stmmac_extra_stats *x) +{ + void __iomem *ioaddr = hw->pcsr; + u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); + u32 intr_mask = readl(ioaddr + GMAC_INT_MASK); + int ret = 0; + + /* Discard masked bits */ + intr_status &= ~intr_mask; + + /* Not used events (e.g. MMC interrupts) are not handled. */ + if ((intr_status & GMAC_INT_STATUS_MMCTIS)) + x->mmc_tx_irq_n++; + if (unlikely(intr_status & GMAC_INT_STATUS_MMCRIS)) + x->mmc_rx_irq_n++; + if (unlikely(intr_status & GMAC_INT_STATUS_MMCCSUM)) + x->mmc_rx_csum_offload_irq_n++; + if (unlikely(intr_status & GMAC_INT_DISABLE_PMT)) { + /* clear the PMT bits 5 and 6 by reading the PMT status reg */ + readl(ioaddr + GMAC_PMT); + x->irq_receive_pmt_irq_n++; + } + + /* MAC tx/rx EEE LPI entry/exit interrupts */ + if (intr_status & GMAC_INT_STATUS_LPIIS) { + /* Clean LPI interrupt by reading the Reg 12 */ + ret = readl(ioaddr + LPI_CTRL_STATUS); + + if (ret & LPI_CTRL_STATUS_TLPIEN) + x->irq_tx_path_in_lpi_mode_n++; + if (ret & LPI_CTRL_STATUS_TLPIEX) + x->irq_tx_path_exit_lpi_mode_n++; + if (ret & LPI_CTRL_STATUS_RLPIEN) + x->irq_rx_path_in_lpi_mode_n++; + if (ret & LPI_CTRL_STATUS_RLPIEX) + x->irq_rx_path_exit_lpi_mode_n++; + } + + dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x); + + if (intr_status & PCS_RGSMIIIS_IRQ) + dwegmac_rgsmii(ioaddr, x); + + return ret; +} + +static void dwegmac_set_eee_mode(struct mac_device_info *hw, + bool en_tx_lpi_clockgating) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + /*TODO - en_tx_lpi_clockgating treatment */ + + /* Enable the link status receive on RGMII, SGMII ore SMII + * receive path and instruct the transmit to enter in LPI + * state. + */ + value = readl(ioaddr + LPI_CTRL_STATUS); + value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA; + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwegmac_reset_eee_mode(struct mac_device_info *hw) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + value = readl(ioaddr + LPI_CTRL_STATUS); + value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA); + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwegmac_set_eee_pls(struct mac_device_info *hw, int link) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + value = readl(ioaddr + LPI_CTRL_STATUS); + + if (link) + value |= LPI_CTRL_STATUS_PLS; + else + value &= ~LPI_CTRL_STATUS_PLS; + + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwegmac_set_eee_timer(struct mac_device_info *hw, int ls, int tw) +{ + void __iomem *ioaddr = hw->pcsr; + int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16); + + /* Program the timers in the LPI timer control register: + * LS: minimum time (ms) for which the link + * status from PHY should be ok before transmitting + * the LPI pattern. + * TW: minimum time (us) for which the core waits + * after it has stopped transmitting the LPI pattern. + */ + writel(value, ioaddr + LPI_TIMER_CTRL); +} + +static void dwegmac_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral, + bool loopback) +{ + dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback); +} + +static void dwegmac_rane(void __iomem *ioaddr, bool restart) +{ + dwmac_rane(ioaddr, GMAC_PCS_BASE, restart); +} + +static void dwegmac_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv) +{ + dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv); +} + +static void dwegmac_debug(struct stmmac_priv *priv, void __iomem *ioaddr, + struct stmmac_extra_stats *x, + u32 rx_queues, u32 tx_queues) +{ + u32 value = readl(ioaddr + GMAC_DEBUG); + + if (value & GMAC_DEBUG_TXSTSFSTS) + x->mtl_tx_status_fifo_full++; + if (value & GMAC_DEBUG_TXFSTS) + x->mtl_tx_fifo_not_empty++; + if (value & GMAC_DEBUG_TWCSTS) + x->mmtl_fifo_ctrl++; + if (value & GMAC_DEBUG_TRCSTS_MASK) { + u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK) + >> GMAC_DEBUG_TRCSTS_SHIFT; + if (trcsts == GMAC_DEBUG_TRCSTS_WRITE) + x->mtl_tx_fifo_read_ctrl_write++; + else if (trcsts == GMAC_DEBUG_TRCSTS_TXW) + x->mtl_tx_fifo_read_ctrl_wait++; + else if (trcsts == GMAC_DEBUG_TRCSTS_READ) + x->mtl_tx_fifo_read_ctrl_read++; + else + x->mtl_tx_fifo_read_ctrl_idle++; + } + if (value & GMAC_DEBUG_TXPAUSED) + x->mac_tx_in_pause++; + if (value & GMAC_DEBUG_TFCSTS_MASK) { + u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK) + >> GMAC_DEBUG_TFCSTS_SHIFT; + + if (tfcsts == GMAC_DEBUG_TFCSTS_XFER) + x->mac_tx_frame_ctrl_xfer++; + else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE) + x->mac_tx_frame_ctrl_pause++; + else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT) + x->mac_tx_frame_ctrl_wait++; + else + x->mac_tx_frame_ctrl_idle++; + } + if (value & GMAC_DEBUG_TPESTS) + x->mac_gmii_tx_proto_engine++; + if (value & GMAC_DEBUG_RXFSTS_MASK) { + u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK) + >> GMAC_DEBUG_RRCSTS_SHIFT; + + if (rxfsts == GMAC_DEBUG_RXFSTS_FULL) + x->mtl_rx_fifo_fill_level_full++; + else if (rxfsts == GMAC_DEBUG_RXFSTS_AT) + x->mtl_rx_fifo_fill_above_thresh++; + else if (rxfsts == GMAC_DEBUG_RXFSTS_BT) + x->mtl_rx_fifo_fill_below_thresh++; + else + x->mtl_rx_fifo_fill_level_empty++; + } + if (value & GMAC_DEBUG_RRCSTS_MASK) { + u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >> + GMAC_DEBUG_RRCSTS_SHIFT; + + if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH) + x->mtl_rx_fifo_read_ctrl_flush++; + else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT) + x->mtl_rx_fifo_read_ctrl_read_data++; + else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA) + x->mtl_rx_fifo_read_ctrl_status++; + else + x->mtl_rx_fifo_read_ctrl_idle++; + } + if (value & GMAC_DEBUG_RWCSTS) + x->mtl_rx_fifo_ctrl_active++; + if (value & GMAC_DEBUG_RFCFCSTS_MASK) + x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK) + >> GMAC_DEBUG_RFCFCSTS_SHIFT; + if (value & GMAC_DEBUG_RPESTS) + x->mac_gmii_rx_proto_engine++; +} + +static void dwegmac_set_mac_loopback(void __iomem *ioaddr, bool enable) +{ + u32 value = readl(ioaddr + GMAC_CONTROL); + + if (enable) + value |= GMAC_CONTROL_LM; + else + value &= ~GMAC_CONTROL_LM; + + writel(value, ioaddr + GMAC_CONTROL); +} + +const struct stmmac_ops dwegmac_ops = { + .core_init = dwegmac_core_init, + .set_mac = stmmac_set_mac, + .rx_ipc = dwegmac_rx_ipc_enable, + .dump_regs = dwegmac_dump_regs, + .host_irq_status = dwegmac_irq_status, + .set_filter = dwegmac_set_filter, + .flow_ctrl = dwegmac_flow_ctrl, + .pmt = dwegmac_pmt, + .set_umac_addr = dwegmac_set_umac_addr, + .get_umac_addr = dwegmac_get_umac_addr, + .set_eee_mode = dwegmac_set_eee_mode, + .reset_eee_mode = dwegmac_reset_eee_mode, + .set_eee_timer = dwegmac_set_eee_timer, + .set_eee_pls = dwegmac_set_eee_pls, + .debug = dwegmac_debug, + .pcs_ctrl_ane = dwegmac_ctrl_ane, + .pcs_rane = dwegmac_rane, + .pcs_get_adv_lp = dwegmac_get_adv_lp, + .set_mac_loopback = dwegmac_set_mac_loopback, +}; + +int dwegmac_setup(struct stmmac_priv *priv) +{ + struct mac_device_info *mac = priv->hw; + + dev_info(priv->device, "\tExtended DWMAC\n"); + + priv->dev->priv_flags |= IFF_UNICAST_FLT; + 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 0; +} diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c new file mode 100644 index 000000000000..9bb0564fbeff --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include <linux/io.h> +#include "stmmac.h" +#include "dwegmac.h" +#include "dwegmac_dma.h" + +static int dwegmac_dma_reset(struct stmmac_priv *priv, void __iomem *ioaddr) +{ + u32 value = readl(ioaddr + DMA_BUS_MODE); + + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + + return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, + !(value & DMA_BUS_MODE_SFT_RESET), + 10000, 200000); +} + +static void dwegmac_enable_dma_transmission(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + writel(1, ioaddr + DMA_CHAN_XMT_POLL_DEMAND(chan)); +} + +static void dwegmac_enable_dma_irq(struct stmmac_priv *priv, + void __iomem *ioaddr, + u32 chan, bool rx, bool tx) +{ + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value |= DMA_INTR_DEFAULT_RX; + if (tx) + value |= DMA_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); +} + +static void dwegmac_disable_dma_irq(struct stmmac_priv *priv, + void __iomem *ioaddr, + u32 chan, bool rx, bool tx) +{ + u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (rx) + value &= ~DMA_INTR_DEFAULT_RX; + if (tx) + value &= ~DMA_INTR_DEFAULT_TX; + + writel(value, ioaddr + DMA_CHAN_INTR_ENA(chan)); +} + +static void dwegmac_dma_start_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); + + value |= DMA_CONTROL_ST; + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); +} + +static void dwegmac_dma_stop_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); + + value &= ~DMA_CONTROL_ST; + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); +} + +static void dwegmac_dma_start_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); + + value |= DMA_CONTROL_SR; + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); +} + +static void dwegmac_dma_stop_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 chan) +{ + u32 value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); + + value &= ~DMA_CONTROL_SR; + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); +} + +static int dwegmac_dma_interrupt(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct stmmac_extra_stats *x, + u32 chan, u32 dir) +{ + int ret = 0; + u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(chan)); + + if (dir == DMA_DIR_RX) + intr_status &= DMA_STATUS_MSK_RX; + else if (dir == DMA_DIR_TX) + intr_status &= DMA_STATUS_MSK_TX; + + /* ABNORMAL interrupts */ + if (unlikely(intr_status & (DMA_STATUS_TX_AIS | DMA_STATUS_RX_AIS))) { + 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 & + (DMA_STATUS_TX_FBI | DMA_STATUS_RX_FBI))) { + x->fatal_bus_error_irq++; + ret = tx_hard_error; + } + } + /* TX/RX NORMAL interrupts */ + if (likely(intr_status & (DMA_STATUS_TX_NIS | DMA_STATUS_RX_NIS))) { + x->normal_irq_n++; + 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)) { + x->rx_normal_irq_n++; + ret |= handle_rx; + } + } + if (likely(intr_status & DMA_STATUS_TI)) { + x->tx_normal_irq_n++; + ret |= handle_tx; + } + if (unlikely(intr_status & DMA_STATUS_ERI)) + x->rx_early_irq++; + } + + writel((intr_status & 0x7ffff), ioaddr + DMA_CHAN_STATUS(chan)); + + return ret; +} + +static void dwegmac_dma_axi(struct stmmac_priv *priv, void __iomem *ioaddr, + struct stmmac_axi *axi) +{ + u32 value = readl(ioaddr + DMA_AXI_BUS_MODE); + int i; + + pr_info("dwegmac: Master AXI performs %s burst length\n", + !(value & DMA_AXI_UNDEF) ? "fixed" : "any"); + + if (axi->axi_lpi_en) + value |= DMA_AXI_EN_LPI; + if (axi->axi_xit_frm) + value |= DMA_AXI_LPI_XIT_FRM; + + if (priv->plat->dma_cfg->dma64) { + value &= ~DMA_AXI_WR_OSR64_LMT; + value |= (axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR64_LMT_MASK) << + DMA_AXI_WR_OSR64_LMT_SHIFT; + + value &= ~DMA_AXI_RD_OSR64_LMT; + value |= (axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR64_LMT_MASK) << + DMA_AXI_RD_OSR64_LMT_SHIFT; + } else { + value &= ~DMA_AXI_WR_OSR_LMT; + value |= (axi->axi_wr_osr_lmt & DMA_AXI_WR_OSR_LMT_MASK) << + DMA_AXI_WR_OSR_LMT_SHIFT; + + value &= ~DMA_AXI_RD_OSR_LMT; + value |= (axi->axi_rd_osr_lmt & DMA_AXI_RD_OSR_LMT_MASK) << + DMA_AXI_RD_OSR_LMT_SHIFT; + } + + /* Depending on the UNDEF bit the Master AXI will perform any burst + * length according to the BLEN programmed (by default all BLEN are + * set). + */ + for (i = 0; i < AXI_BLEN; i++) { + switch (axi->axi_blen[i]) { + case 256: + value |= DMA_AXI_BLEN256; + break; + case 128: + value |= DMA_AXI_BLEN128; + break; + case 64: + value |= DMA_AXI_BLEN64; + break; + case 32: + value |= DMA_AXI_BLEN32; + break; + case 16: + value |= DMA_AXI_BLEN16; + break; + case 8: + value |= DMA_AXI_BLEN8; + break; + case 4: + value |= DMA_AXI_BLEN4; + break; + } + } + + writel(value, ioaddr + DMA_AXI_BUS_MODE); +} + +static void dwegmac_dma_init(struct stmmac_priv *priv, void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, int atds) +{ + u32 value = readl(ioaddr + DMA_BUS_MODE); + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; + + /* 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 (atds) + value |= DMA_BUS_MODE_ATDS; + + if (dma_cfg->aal) + value |= DMA_BUS_MODE_AAL; + + writel(value, ioaddr + DMA_BUS_MODE); + + /* Mask interrupts by writing to CSR7 */ + writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); +} + +static void dwegmac_dma_init_channel(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 chan) +{ + u32 value; + int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; + int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; + + if (!priv->plat->multi_msi_en) + return; + + /* 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; + + 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, ioaddr + DMA_CHAN_INTR_ENA(chan)); + + if (dma_cfg->dma64) + writel(0x100, ioaddr + DMA_CHAN_NEWFUNC_CONFIG(chan)); +} + +static void dwegmac_dma_init_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + dma_addr_t dma_rx_phy, u32 chan) +{ + if (dma_cfg->dma64) { + writel(lower_32_bits(dma_rx_phy), ioaddr + + DMA_CHAN_RCV_BASE_ADDR64(chan)); + writel(upper_32_bits(dma_rx_phy), ioaddr + + DMA_CHAN_RCV_BASE_ADDR64_HI(chan)); + writel(upper_32_bits(dma_rx_phy), ioaddr + + DMA_RCV_BASE_ADDR64_HI_SHADOW1); + writel(upper_32_bits(dma_rx_phy), ioaddr + + DMA_RCV_BASE_ADDR64_HI_SHADOW2); + } else { + writel(lower_32_bits(dma_rx_phy), ioaddr + + DMA_CHAN_RCV_BASE_ADDR(chan)); + } +} + +static void dwegmac_dma_init_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + dma_addr_t dma_tx_phy, u32 chan) +{ + if (dma_cfg->dma64) { + writel(lower_32_bits(dma_tx_phy), ioaddr + + DMA_CHAN_TX_BASE_ADDR64(chan)); + writel(upper_32_bits(dma_tx_phy), ioaddr + + DMA_CHAN_TX_BASE_ADDR64_HI(chan)); + } else { + writel(lower_32_bits(dma_tx_phy), ioaddr + + DMA_CHAN_TX_BASE_ADDR(chan)); + } +} + +static u32 dwegmac_configure_fc(u32 csr6, int rxfifosz) +{ + csr6 &= ~DMA_CONTROL_RFA_MASK; + csr6 &= ~DMA_CONTROL_RFD_MASK; + + /* Leave flow control disabled if receive fifo size is less than + * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full, + * and send XON when 2K less than full. + */ + if (rxfifosz < 4096) { + csr6 &= ~DMA_CONTROL_EFC; + pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n", + rxfifosz); + } else { + csr6 |= DMA_CONTROL_EFC; + csr6 |= RFA_FULL_MINUS_1K; + csr6 |= RFD_FULL_MINUS_2K; + } + return csr6; +} + +static void dwegmac_dma_operation_mode_rx(struct stmmac_priv *priv, + void __iomem *ioaddr, int mode, + u32 channel, int fifosz, u8 qmode) +{ + u32 csr6 = readl(ioaddr + DMA_CHAN_CONTROL(channel)); + + if (mode == SF_DMA_MODE) { + pr_debug("GMAC: enable RX store and forward mode\n"); + csr6 |= DMA_CONTROL_RSF; + } else { + pr_debug("GMAC: disable RX SF mode (threshold %d)\n", mode); + csr6 &= ~DMA_CONTROL_RSF; + csr6 &= DMA_CONTROL_TC_RX_MASK; + if (mode <= 32) + csr6 |= DMA_CONTROL_RTC_32; + else if (mode <= 64) + csr6 |= DMA_CONTROL_RTC_64; + else if (mode <= 96) + csr6 |= DMA_CONTROL_RTC_96; + else + csr6 |= DMA_CONTROL_RTC_128; + } + + /* Configure flow control based on rx fifo size */ + csr6 = dwegmac_configure_fc(csr6, fifosz); + + writel(csr6, ioaddr + DMA_CHAN_CONTROL(channel)); +} + +static void dwegmac_dma_operation_mode_tx(struct stmmac_priv *priv, + void __iomem *ioaddr, int mode, + u32 channel, int fifosz, u8 qmode) +{ + u32 csr6 = readl(ioaddr + DMA_CHAN_CONTROL(channel)); + + if (mode == SF_DMA_MODE) { + pr_debug("GMAC: enable TX store and forward mode\n"); + /* Transmit COE type 2 cannot be done in cut-through mode. */ + csr6 |= DMA_CONTROL_TSF; + /* Operating on second frame increase the performance + * especially when transmit store-and-forward is used. + */ + csr6 |= DMA_CONTROL_OSF; + } else { + pr_debug("GMAC: disabling TX SF (threshold %d)\n", mode); + csr6 &= ~DMA_CONTROL_TSF; + csr6 &= DMA_CONTROL_TC_TX_MASK; + /* Set the transmit threshold */ + if (mode <= 32) + csr6 |= DMA_CONTROL_TTC_32; + else if (mode <= 64) + csr6 |= DMA_CONTROL_TTC_64; + else if (mode <= 128) + csr6 |= DMA_CONTROL_TTC_128; + else if (mode <= 192) + csr6 |= DMA_CONTROL_TTC_192; + else + csr6 |= DMA_CONTROL_TTC_256; + } + + writel(csr6, ioaddr + DMA_CHAN_CONTROL(channel)); +} + +static void dwegmac_dump_dma_regs(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 *reg_space) +{ + int i; + + for (i = 0; i < NUM_DWEGMAC_DMA_REGS; i++) + if (i < 12 || i > 17) + reg_space[DMA_BUS_MODE / 4 + i] = + readl(ioaddr + DMA_BUS_MODE + i * 4); +} + +static int dwegmac_get_hw_feature(struct stmmac_priv *priv, + void __iomem *ioaddr, + struct dma_features *dma_cap) +{ + u32 hw_cap = readl(ioaddr + DMA_HW_FEATURE); + + if (!hw_cap) { + /* 0x00000000 is the value read on old hardware that does not + * implement this register + */ + return -EOPNOTSUPP; + } + + dma_cap->mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL); + dma_cap->mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1; + dma_cap->half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2; + dma_cap->hash_filter = (hw_cap & DMA_HW_FEAT_HASHSEL) >> 4; + dma_cap->multi_addr = (hw_cap & DMA_HW_FEAT_ADDMAC) >> 5; + dma_cap->pcs = (hw_cap & DMA_HW_FEAT_PCSSEL) >> 6; + dma_cap->sma_mdio = (hw_cap & DMA_HW_FEAT_SMASEL) >> 8; + dma_cap->pmt_remote_wake_up = (hw_cap & DMA_HW_FEAT_RWKSEL) >> 9; + dma_cap->pmt_magic_frame = (hw_cap & DMA_HW_FEAT_MGKSEL) >> 10; + /* MMC */ + dma_cap->rmon = (hw_cap & DMA_HW_FEAT_MMCSEL) >> 11; + /* IEEE 1588-2002 */ + dma_cap->time_stamp = + (hw_cap & DMA_HW_FEAT_TSVER1SEL) >> 12; + /* IEEE 1588-2008 */ + dma_cap->atime_stamp = (hw_cap & DMA_HW_FEAT_TSVER2SEL) >> 13; + /* 802.3az - Energy-Efficient Ethernet (EEE) */ + dma_cap->eee = (hw_cap & DMA_HW_FEAT_EEESEL) >> 14; + dma_cap->av = (hw_cap & DMA_HW_FEAT_AVSEL) >> 15; + /* TX and RX csum */ + dma_cap->tx_coe = (hw_cap & DMA_HW_FEAT_TXCOESEL) >> 16; + dma_cap->rx_coe_type1 = (hw_cap & DMA_HW_FEAT_RXTYP1COE) >> 17; + dma_cap->rx_coe_type2 = (hw_cap & DMA_HW_FEAT_RXTYP2COE) >> 18; + dma_cap->rxfifo_over_2048 = (hw_cap & DMA_HW_FEAT_RXFIFOSIZE) >> 19; + /* TX and RX number of channels */ + dma_cap->number_rx_channel = (hw_cap & DMA_HW_FEAT_RXCHCNT) >> 20; + dma_cap->number_tx_channel = (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22; + /* Alternate (enhanced) DESC mode */ + dma_cap->enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24; + + return 0; +} + +static void dwegmac_rx_watchdog(struct stmmac_priv *priv, + void __iomem *ioaddr, u32 riwt, u32 queue) +{ + writel(riwt, ioaddr + DMA_CHAN_RX_WATCHDOG(queue)); +} + +const struct stmmac_dma_ops dwegmac_dma_ops = { + .reset = dwegmac_dma_reset, + .init = dwegmac_dma_init, + .init_chan = dwegmac_dma_init_channel, + .init_rx_chan = dwegmac_dma_init_rx, + .init_tx_chan = dwegmac_dma_init_tx, + .axi = dwegmac_dma_axi, + .dump_regs = dwegmac_dump_dma_regs, + .dma_rx_mode = dwegmac_dma_operation_mode_rx, + .dma_tx_mode = dwegmac_dma_operation_mode_tx, + .enable_dma_transmission = dwegmac_enable_dma_transmission, + .enable_dma_irq = dwegmac_enable_dma_irq, + .disable_dma_irq = dwegmac_disable_dma_irq, + .start_tx = dwegmac_dma_start_tx, + .stop_tx = dwegmac_dma_stop_tx, + .start_rx = dwegmac_dma_start_rx, + .stop_rx = dwegmac_dma_stop_rx, + .dma_interrupt = dwegmac_dma_interrupt, + .get_hw_feature = dwegmac_get_hw_feature, + .rx_watchdog = dwegmac_rx_watchdog, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h new file mode 100644 index 000000000000..aadc13eae502 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwegmac_dma.h @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef __DWEGMAC_DMA_H__ +#define __DWEGMAC_DMA_H__ + +/* DMA CRS Control and Status Register Mapping */ +#define DMA_BUS_MODE 0x00001000 /* Bus Mode */ +#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */ +#define DMA_RCV_POLL_DEMAND 0x00001008 /* Received Poll Demand */ +#define DMA_RCV_BASE_ADDR 0x0000100c /* Receive List Base */ +#define DMA_RCV_BASE_ADDR64 0x00001090 +#define DMA_RCV_BASE_ADDR64_HI 0x00001094 +#define DMA_RCV_BASE_ADDR64_HI_SHADOW1 0x00001068 +#define DMA_RCV_BASE_ADDR64_HI_SHADOW2 0x000010a8 +#define DMA_TX_BASE_ADDR 0x00001010 /* Transmit List Base */ +#define DMA_TX_BASE_ADDR64 0x00001098 +#define DMA_TX_BASE_ADDR64_HI 0x0000109c +#define DMA_STATUS 0x00001014 /* Status Register */ +#define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ +#define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ +#define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ +#define DMA_NEWFUNC_CONFIG 0x00001080 /* New Function Config */ + +/* SW Reset */ +#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ + +/* Rx watchdog register */ +#define DMA_RX_WATCHDOG 0x00001024 + +/* AXI Master Bus Mode */ +#define DMA_AXI_BUS_MODE 0x00001028 + +#define DMA_AXI_EN_LPI BIT(31) +#define DMA_AXI_LPI_XIT_FRM BIT(30) +#define DMA_AXI_WR_OSR_LMT GENMASK(23, 20) +#define DMA_AXI_WR_OSR_LMT_SHIFT 20 +#define DMA_AXI_WR_OSR_LMT_MASK 0xf +#define DMA_AXI_RD_OSR_LMT GENMASK(19, 16) +#define DMA_AXI_RD_OSR_LMT_SHIFT 16 +#define DMA_AXI_RD_OSR_LMT_MASK 0xf +#define DMA_AXI_WR_OSR64_LMT GENMASK(21, 20) +#define DMA_AXI_WR_OSR64_LMT_SHIFT 20 +#define DMA_AXI_WR_OSR64_LMT_MASK 0x3 +#define DMA_AXI_RD_OSR64_LMT GENMASK(17, 16) +#define DMA_AXI_RD_OSR64_LMT_SHIFT 16 +#define DMA_AXI_RD_OSR64_LMT_MASK 0x3 + +#define DMA_AXI_OSR_MAX 0xf +#define DMA_AXI_MAX_OSR_LIMIT ((DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT) | \ + (DMA_AXI_OSR_MAX << DMA_AXI_RD_OSR_LMT_SHIFT)) +#define DMA_AXI_OSR64_MAX 0x3 +#define DMA_AXI_MAX_OSR64_LIMIT ((DMA_AXI_OSR64_MAX << DMA_AXI_WR_OSR64_LMT_SHIFT) | \ + (DMA_AXI_OSR64_MAX << DMA_AXI_RD_OSR64_LMT_SHIFT)) +#define DMA_AXI_1KBBE BIT(13) +#define DMA_AXI_AAL BIT(12) +#define DMA_AXI_BLEN256 BIT(7) +#define DMA_AXI_BLEN128 BIT(6) +#define DMA_AXI_BLEN64 BIT(5) +#define DMA_AXI_BLEN32 BIT(4) +#define DMA_AXI_BLEN16 BIT(3) +#define DMA_AXI_BLEN8 BIT(2) +#define DMA_AXI_BLEN4 BIT(1) +#define DMA_BURST_LEN_DEFAULT (DMA_AXI_BLEN256 | DMA_AXI_BLEN128 | \ + DMA_AXI_BLEN64 | DMA_AXI_BLEN32 | \ + DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | \ + DMA_AXI_BLEN4) + +#define DMA_AXI_UNDEF BIT(0) + +#define DMA_AXI_BURST_LEN_MASK 0x000000fe + +#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */ +#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */ +#define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */ + +/* DMA Control register defines */ +#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ +#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ + +/* DMA Normal interrupt */ +#define DMA_INTR_ENA_NIE 0x00060000 /* Normal Summary */ +#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */ +#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */ +#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */ + +#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \ + DMA_INTR_ENA_TIE) + +/* DMA Abnormal interrupt */ +#define DMA_INTR_ENA_AIE 0x00018000 /* Abnormal Summary */ +#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */ +#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */ +#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */ +#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */ +#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */ +#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */ +#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */ +#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */ + +#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \ + DMA_INTR_ENA_UNE) + +/* DMA default interrupt mask */ +#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) +#define DMA_INTR_DEFAULT_RX (DMA_INTR_ENA_RIE) +#define DMA_INTR_DEFAULT_TX (DMA_INTR_ENA_TIE) + +/* DMA Status register defines */ +#define DMA_STATUS_GLPII 0x10000000 /* GMAC LPI interrupt */ +#define DMA_STATUS_EB_MASK 0x0e000000 /* Error Bits Mask */ +#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ +#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ +#define DMA_STATUS_TS_MASK 0x01c00000 /* Transmit Process State */ +#define DMA_STATUS_TS_SHIFT 22 +#define DMA_STATUS_RS_MASK 0x00380000 /* Receive Process State */ +#define DMA_STATUS_RS_SHIFT 19 +#define DMA_STATUS_TX_NIS 0x00040000 /* Normal Tx Interrupt Summary */ +#define DMA_STATUS_RX_NIS 0x00020000 /* Normal Rx Interrupt Summary */ +#define DMA_STATUS_TX_AIS 0x00010000 /* Abnormal Tx Interrupt Summary */ +#define DMA_STATUS_RX_AIS 0x00008000 /* Abnormal Rx Interrupt Summary */ +#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ +#define DMA_STATUS_TX_FBI 0x00002000 /* Fatal Tx Bus Error Interrupt */ +#define DMA_STATUS_RX_FBI 0x00001000 /* Fatal Rx Bus Error Interrupt */ +#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ +#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ +#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ +#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ +#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ +#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ +#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ +#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ +#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ + +#define DMA_STATUS_MSK_RX_COMMON (DMA_STATUS_RX_NIS | \ + DMA_STATUS_RX_AIS | \ + DMA_STATUS_RX_FBI) + +#define DMA_STATUS_MSK_TX_COMMON (DMA_STATUS_TX_NIS | \ + DMA_STATUS_TX_AIS | \ + DMA_STATUS_TX_FBI) + +#define DMA_STATUS_MSK_RX (DMA_STATUS_ERI | \ + DMA_STATUS_RWT | \ + DMA_STATUS_RPS | \ + DMA_STATUS_RU | \ + DMA_STATUS_RI | \ + DMA_STATUS_OVF | \ + DMA_STATUS_MSK_RX_COMMON) + +#define DMA_STATUS_MSK_TX (DMA_STATUS_ETI | \ + DMA_STATUS_UNF | \ + DMA_STATUS_TJT | \ + DMA_STATUS_TU | \ + DMA_STATUS_TPS | \ + DMA_STATUS_TI | \ + DMA_STATUS_MSK_TX_COMMON) + +/* Following DMA defines are chanels oriented */ +#define DMA_CHAN_OFFSET 0x100 + +static inline u32 dma_chan_base_addr(u32 base, u32 chan) +{ + return base + chan * DMA_CHAN_OFFSET; +} + +#define DMA_CHAN_NEWFUNC_CONFIG(chan) dma_chan_base_addr(DMA_NEWFUNC_CONFIG, chan) +#define DMA_CHAN_XMT_POLL_DEMAND(chan) dma_chan_base_addr(DMA_XMT_POLL_DEMAND, chan) +#define DMA_CHAN_INTR_ENA(chan) dma_chan_base_addr(DMA_INTR_ENA, chan) +#define DMA_CHAN_CONTROL(chan) dma_chan_base_addr(DMA_CONTROL, chan) +#define DMA_CHAN_STATUS(chan) dma_chan_base_addr(DMA_STATUS, chan) +#define DMA_CHAN_BUS_MODE(chan) dma_chan_base_addr(DMA_BUS_MODE, chan) +#define DMA_CHAN_RCV_BASE_ADDR(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR, chan) +#define DMA_CHAN_RCV_BASE_ADDR64(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR64, chan) +#define DMA_CHAN_RCV_BASE_ADDR64_HI(chan) dma_chan_base_addr(DMA_RCV_BASE_ADDR64_HI, chan) +#define DMA_CHAN_TX_BASE_ADDR(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR, chan) +#define DMA_CHAN_TX_BASE_ADDR64(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR64, chan) +#define DMA_CHAN_TX_BASE_ADDR64_HI(chan) dma_chan_base_addr(DMA_TX_BASE_ADDR64_HI, chan) +#define DMA_CHAN_RX_WATCHDOG(chan) dma_chan_base_addr(DMA_RX_WATCHDOG, chan) + +#define NUM_DWEGMAC_DMA_REGS 23 + +#endif /* __DWEGMAC_DMA_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c index c5768bbec38e..4674af3d78cb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.c +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c @@ -61,7 +61,7 @@ static int stmmac_dwmac1_quirks(struct stmmac_priv *priv) dev_info(priv->device, "Enhanced/Alternate descriptors\n"); /* GMAC older than 3.50 has no extended descriptors */ - if (priv->synopsys_id >= DWMAC_CORE_3_50) { + if (priv->synopsys_id >= DWMAC_CORE_3_50 || priv->plat->has_egmac) { dev_info(priv->device, "Enabled extended descriptors\n"); priv->extend_desc = 1; } else { @@ -107,6 +107,7 @@ static const struct stmmac_hwif_entry { bool gmac; bool gmac4; bool xgmac; + bool egmac; u32 min_id; u32 dev_id; const struct stmmac_regs_off regs; @@ -125,6 +126,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = false, .xgmac = false, + .egmac = false, .min_id = 0, .regs = { .ptp_off = PTP_GMAC3_X_OFFSET, @@ -143,6 +145,7 @@ static const struct stmmac_hwif_entry { .gmac = true, .gmac4 = false, .xgmac = false, + .egmac = false, .min_id = 0, .regs = { .ptp_off = PTP_GMAC3_X_OFFSET, @@ -161,6 +164,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = true, .xgmac = false, + .egmac = false, .min_id = 0, .regs = { .ptp_off = PTP_GMAC4_OFFSET, @@ -179,6 +183,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = true, .xgmac = false, + .egmac = false, .min_id = DWMAC_CORE_4_00, .regs = { .ptp_off = PTP_GMAC4_OFFSET, @@ -197,6 +202,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = true, .xgmac = false, + .egmac = false, .min_id = DWMAC_CORE_4_10, .regs = { .ptp_off = PTP_GMAC4_OFFSET, @@ -215,6 +221,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = true, .xgmac = false, + .egmac = false, .min_id = DWMAC_CORE_5_10, .regs = { .ptp_off = PTP_GMAC4_OFFSET, @@ -233,6 +240,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = false, .xgmac = true, + .egmac = false, .min_id = DWXGMAC_CORE_2_10, .dev_id = DWXGMAC_ID, .regs = { @@ -252,6 +260,7 @@ static const struct stmmac_hwif_entry { .gmac = false, .gmac4 = false, .xgmac = true, + .egmac = false, .min_id = DWXLGMAC_CORE_2_00, .dev_id = DWXLGMAC_ID, .regs = { @@ -267,11 +276,50 @@ static const struct stmmac_hwif_entry { .mmc = &dwxgmac_mmc_ops, .setup = dwxlgmac2_setup, .quirks = stmmac_dwxlgmac_quirks, + }, { + .gmac = false, + .gmac4 = false, + .xgmac = false, + .egmac = true, + .min_id = DWEGMAC_CORE_1_00, + .regs = { + .ptp_off = PTP_GMAC3_X_OFFSET, + .mmc_off = MMC_GMAC3_X_OFFSET, + }, + .desc = NULL, + .dma = &dwegmac_dma_ops, + .mac = &dwegmac_ops, + .hwtimestamp = &stmmac_ptp, + .mode = NULL, + .tc = NULL, + .mmc = &dwmac_mmc_ops, + .setup = dwegmac_setup, + .quirks = stmmac_dwmac1_quirks, + }, { + .gmac = false, + .gmac4 = false, + .xgmac = false, + .egmac = true, + .min_id = DWMAC_CORE_3_50, + .regs = { + .ptp_off = PTP_GMAC3_X_OFFSET, + .mmc_off = MMC_GMAC3_X_OFFSET, + }, + .desc = NULL, + .dma = &dwmac1000_dma_ops, + .mac = &dwmac1000_ops, + .hwtimestamp = &stmmac_ptp, + .mode = NULL, + .tc = NULL, + .mmc = &dwmac_mmc_ops, + .setup = dwmac1000_setup, + .quirks = stmmac_dwmac1_quirks, }, }; int stmmac_hwif_init(struct stmmac_priv *priv) { + bool needs_egmac = priv->plat->has_egmac; bool needs_xgmac = priv->plat->has_xgmac; bool needs_gmac4 = priv->plat->has_gmac4; bool needs_gmac = priv->plat->has_gmac; @@ -281,7 +329,7 @@ int stmmac_hwif_init(struct stmmac_priv *priv) u32 id, dev_id = 0; int i, ret; - if (needs_gmac) { + if (needs_gmac || needs_egmac) { id = stmmac_get_id(priv, GMAC_VERSION); } else if (needs_gmac4 || needs_xgmac) { id = stmmac_get_id(priv, GMAC4_VERSION); @@ -321,6 +369,8 @@ int stmmac_hwif_init(struct stmmac_priv *priv) continue; if (needs_xgmac ^ entry->xgmac) continue; + if (needs_egmac ^ entry->egmac) + continue; /* Use synopsys_id var because some setups can override this */ if (priv->synopsys_id < entry->min_id) continue; diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 00be9a7003c8..41989903e8c9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -667,6 +667,8 @@ extern const struct stmmac_dma_ops dwxgmac210_dma_ops; extern const struct stmmac_desc_ops dwxgmac210_desc_ops; extern const struct stmmac_mmc_ops dwmac_mmc_ops; extern const struct stmmac_mmc_ops dwxgmac_mmc_ops; +extern const struct stmmac_ops dwegmac_ops; +extern const struct stmmac_dma_ops dwegmac_dma_ops; #define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ #define GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 2ae73ab842d4..dba33ce392a8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -596,7 +596,7 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, priv->xstats.phy_eee_wakeup_error_n = val; } - if (priv->synopsys_id >= DWMAC_CORE_3_50) + if (priv->synopsys_id >= DWMAC_CORE_3_50 || priv->plat->has_egmac) stmmac_mac_debug(priv, priv->ioaddr, (void *)&priv->xstats, rx_queues_count, tx_queues_count); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e8619853b6d6..f91dd3f69fef 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -6936,7 +6936,8 @@ static int stmmac_hw_init(struct stmmac_priv *priv) * riwt_off field from the platform. */ if (((priv->synopsys_id >= DWMAC_CORE_3_50) || - (priv->plat->has_xgmac)) && (!priv->plat->riwt_off)) { + (priv->plat->has_xgmac) || (priv->plat->has_egmac)) && + (!priv->plat->riwt_off)) { priv->use_riwt = 1; dev_info(priv->device, "Enable RX Mitigation via HW Watchdog Timer\n"); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 2fcd83f6db14..0e36259a9568 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -295,5 +295,6 @@ struct plat_stmmacenet_data { bool serdes_up_after_phy_linkup; const struct dwmac4_addrs *dwmac4_addrs; bool has_integrated_pcs; + int has_egmac; }; #endif