[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <193454B6B44560D1+aK-x9J2EIB5aA9yr@LT-Guozexi>
Date: Thu, 28 Aug 2025 09:33:40 +0800
From: Troy Mitchell <troy.mitchell@...ux.spacemit.com>
To: Vivian Wang <wangruikang@...as.ac.cn>,
Andrew Lunn <andrew+netdev@...n.ch>,
Jakub Kicinski <kuba@...nel.org>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Yixun Lan <dlan@...too.org>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>, Paolo Abeni <pabeni@...hat.com>,
Philipp Zabel <p.zabel@...gutronix.de>,
Paul Walmsley <paul.walmsley@...ive.com>,
Palmer Dabbelt <palmer@...belt.com>,
Albert Ou <aou@...s.berkeley.edu>, Alexandre Ghiti <alex@...ti.fr>
Cc: Vivian Wang <uwu@...m.page>,
Vadim Fedorenko <vadim.fedorenko@...ux.dev>,
Junhui Liu <junhui.liu@...moral.tech>,
Simon Horman <horms@...nel.org>,
Maxime Chevallier <maxime.chevallier@...tlin.com>,
netdev@...r.kernel.org, devicetree@...r.kernel.org,
linux-riscv@...ts.infradead.org, spacemit@...ts.linux.dev,
linux-kernel@...r.kernel.org,
Troy Mitchell <troy.mitchell@...ux.spacemit.com>
Subject: Re: [PATCH net-next v7 2/5] net: spacemit: Add K1 Ethernet MAC
On Tue, Aug 26, 2025 at 02:23:47PM +0800, Vivian Wang wrote:
> The Ethernet MACs found on SpacemiT K1 appears to be a custom design
> that only superficially resembles some other embedded MACs. SpacemiT
> refers to them as "EMAC", so let's just call the driver "k1_emac".
>
> Supports RGMII and RMII interfaces. Includes support for MAC hardware
> statistics counters. PTP support is not implemented.
>
> Tested-by: Junhui Liu <junhui.liu@...moral.tech>
> Signed-off-by: Vivian Wang <wangruikang@...as.ac.cn>
> Reviewed-by: Maxime Chevallier <maxime.chevallier@...tlin.com>
> Reviewed-by: Vadim Fedorenko <vadim.fedorenko@...ux.dev>
> ---
Hi Vivian,
I have tested your patch on MUSE-PI.
So here:
Tested-by: Troy Mitchell <troy.mitchell@...ux.spacemit.com>
But I still have a minor style comment on the code.
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/spacemit/Kconfig | 29 +
> drivers/net/ethernet/spacemit/Makefile | 6 +
> drivers/net/ethernet/spacemit/k1_emac.c | 2193 +++++++++++++++++++++++++++++++
> drivers/net/ethernet/spacemit/k1_emac.h | 426 ++++++
> 6 files changed, 2656 insertions(+)
>
> diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..9e558d5893cfbbda0baa7ad21a7209dadda9487e
> --- /dev/null
> +++ b/drivers/net/ethernet/spacemit/k1_emac.c
> @@ -0,0 +1,2193 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SpacemiT K1 Ethernet driver
> + *
> + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
> + * Copyright (C) 2025 Vivian Wang <wangruikang@...as.ac.cn>
> + */
> +
[...]
> +
> +static void emac_wr(struct emac_priv *priv, u32 reg, u32 val)
> +{
> + writel(val, priv->iobase + reg);
> +}
basically short and obvious code like this:
writel(val, priv->iobase + reg)
you replace:
emac_wr(priv, reg, val)
It's not helpful..You could just use writel/readl directly
> +
> +static int emac_rd(struct emac_priv *priv, u32 reg)
> +{
> + return readl(priv->iobase + reg);
> +}
ditto.
> +
> +static int emac_phy_interface_config(struct emac_priv *priv)
> +{
> + u32 val = 0, mask = PHY_INTF_RGMII;
> +
> + switch (priv->phy_interface) {
> + case PHY_INTERFACE_MODE_RMII:
> + mask |= REF_CLK_SEL;
> + break;
> + case PHY_INTERFACE_MODE_RGMII:
> + case PHY_INTERFACE_MODE_RGMII_ID:
> + case PHY_INTERFACE_MODE_RGMII_RXID:
> + case PHY_INTERFACE_MODE_RGMII_TXID:
> + val |= PHY_INTF_RGMII;
> +
> + mask |= RGMII_TX_CLK_SEL;
> + break;
> + default:
> + netdev_err(priv->ndev, "Unsupported PHY interface %d",
> + priv->phy_interface);
> + return -EINVAL;
> + }
> +
> + regmap_update_bits(priv->regmap_apmu,
> + priv->regmap_apmu_offset + APMU_EMAC_CTRL_REG,
> + mask, val);
> +
> + return 0;
> +}
> +
[...]
> +static bool emac_tx_should_interrupt(struct emac_priv *priv, u32 pkt_num)
> +{
> + bool should_interrupt;
> +
> + /* Manage TX mitigation */
> + priv->tx_count_frames += pkt_num;
> + if (likely(priv->tx_coal_frames > priv->tx_count_frames)) {
> + emac_tx_coal_timer_resched(priv);
> + should_interrupt = false;
> + } else {
> + priv->tx_count_frames = 0;
> + should_interrupt = true;
> + }
> +
> + return should_interrupt;
If we really need this variable?
How about this:
if (likely(priv->tx_coal_frames > priv->tx_count_frames)) {
emac_tx_coal_timer_resched(priv);
return false;
}
priv->tx_count_frames = 0;
return true;
> +}
> +
> +static void emac_free_tx_buf(struct emac_priv *priv, int i)
> +{
> + struct emac_tx_desc_buffer *tx_buf;
> + struct emac_desc_ring *tx_ring;
> + struct desc_buf *buf;
> + int j;
> +
> + tx_ring = &priv->tx_ring;
> + tx_buf = &tx_ring->tx_desc_buf[i];
> +
> + for (j = 0; j < 2; j++) {
> + buf = &tx_buf->buf[j];
> + if (buf->dma_addr) {
> + if (buf->map_as_page)
> + dma_unmap_page(&priv->pdev->dev, buf->dma_addr,
> + buf->dma_len, DMA_TO_DEVICE);
> + else
> + dma_unmap_single(&priv->pdev->dev,
> + buf->dma_addr, buf->dma_len,
> + DMA_TO_DEVICE);
> +
> + buf->dma_addr = 0;
> + buf->map_as_page = false;
> + buf->buff_addr = NULL;
> + }
if (!buf->dma_addr)
continue;
Best regards,
Troy
> + }
> +
> + if (tx_buf->skb) {
> + dev_kfree_skb_any(tx_buf->skb);
> + tx_buf->skb = NULL;
> + }
> +}
> +
> +static void emac_clean_tx_desc_ring(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *tx_ring = &priv->tx_ring;
> + u32 i;
> +
> + /* Free all the TX ring skbs */
> + for (i = 0; i < tx_ring->total_cnt; i++)
> + emac_free_tx_buf(priv, i);
> +
> + tx_ring->head = 0;
> + tx_ring->tail = 0;
> +}
> +
> +static void emac_clean_rx_desc_ring(struct emac_priv *priv)
> +{
> + struct emac_rx_desc_buffer *rx_buf;
> + struct emac_desc_ring *rx_ring;
> + u32 i;
> +
> + rx_ring = &priv->rx_ring;
> +
> + /* Free all the RX ring skbs */
> + for (i = 0; i < rx_ring->total_cnt; i++) {
> + rx_buf = &rx_ring->rx_desc_buf[i];
> + if (rx_buf->skb) {
> + dma_unmap_single(&priv->pdev->dev, rx_buf->dma_addr,
> + rx_buf->dma_len, DMA_FROM_DEVICE);
> +
> + dev_kfree_skb(rx_buf->skb);
> + rx_buf->skb = NULL;
> + }
if (!rx_buf->skb)
continue;
> + }
> +
> + rx_ring->tail = 0;
> + rx_ring->head = 0;
> +}
> +
> +static int emac_alloc_tx_resources(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *tx_ring = &priv->tx_ring;
> + struct platform_device *pdev = priv->pdev;
> + u32 size;
> +
> + size = sizeof(struct emac_tx_desc_buffer) * tx_ring->total_cnt;
> +
> + tx_ring->tx_desc_buf = kzalloc(size, GFP_KERNEL);
> + if (!tx_ring->tx_desc_buf)
> + return -ENOMEM;
> +
> + tx_ring->total_size = tx_ring->total_cnt * sizeof(struct emac_desc);
> + tx_ring->total_size = ALIGN(tx_ring->total_size, PAGE_SIZE);
> +
> + tx_ring->desc_addr = dma_alloc_coherent(&pdev->dev, tx_ring->total_size,
> + &tx_ring->desc_dma_addr,
> + GFP_KERNEL);
> + if (!tx_ring->desc_addr) {
> + kfree(tx_ring->tx_desc_buf);
> + return -ENOMEM;
> + }
> +
> + tx_ring->head = 0;
> + tx_ring->tail = 0;
> +
> + return 0;
> +}
> +
> +static int emac_alloc_rx_resources(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *rx_ring = &priv->rx_ring;
> + struct platform_device *pdev = priv->pdev;
> + u32 buf_len;
> +
> + buf_len = sizeof(struct emac_rx_desc_buffer) * rx_ring->total_cnt;
> +
> + rx_ring->rx_desc_buf = kzalloc(buf_len, GFP_KERNEL);
> + if (!rx_ring->rx_desc_buf)
> + return -ENOMEM;
> +
> + rx_ring->total_size = rx_ring->total_cnt * sizeof(struct emac_desc);
> +
> + rx_ring->total_size = ALIGN(rx_ring->total_size, PAGE_SIZE);
> +
> + rx_ring->desc_addr = dma_alloc_coherent(&pdev->dev, rx_ring->total_size,
> + &rx_ring->desc_dma_addr,
> + GFP_KERNEL);
> + if (!rx_ring->desc_addr) {
> + kfree(rx_ring->rx_desc_buf);
> + return -ENOMEM;
> + }
> +
> + rx_ring->head = 0;
> + rx_ring->tail = 0;
> +
> + return 0;
> +}
> +
> +static void emac_free_tx_resources(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *tr = &priv->tx_ring;
> + struct device *dev = &priv->pdev->dev;
> +
> + emac_clean_tx_desc_ring(priv);
> +
> + kfree(tr->tx_desc_buf);
> + tr->tx_desc_buf = NULL;
> +
> + dma_free_coherent(dev, tr->total_size, tr->desc_addr,
> + tr->desc_dma_addr);
> + tr->desc_addr = NULL;
> +}
> +
> +static void emac_free_rx_resources(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *rr = &priv->rx_ring;
> + struct device *dev = &priv->pdev->dev;
> +
> + emac_clean_rx_desc_ring(priv);
> +
> + kfree(rr->rx_desc_buf);
> + rr->rx_desc_buf = NULL;
> +
> + dma_free_coherent(dev, rr->total_size, rr->desc_addr,
> + rr->desc_dma_addr);
> + rr->desc_addr = NULL;
> +}
> +
> +static int emac_tx_clean_desc(struct emac_priv *priv)
> +{
> + struct net_device *ndev = priv->ndev;
> + struct emac_desc_ring *tx_ring;
> + struct emac_desc *tx_desc;
> + u32 i;
> +
> + netif_tx_lock(ndev);
> +
> + tx_ring = &priv->tx_ring;
> +
> + i = tx_ring->tail;
> +
> + while (i != tx_ring->head) {
> + tx_desc = &((struct emac_desc *)tx_ring->desc_addr)[i];
> +
> + /* Stop checking if desc still own by DMA */
> + if (READ_ONCE(tx_desc->desc0) & TX_DESC_0_OWN)
> + break;
> +
> + emac_free_tx_buf(priv, i);
> + memset(tx_desc, 0, sizeof(struct emac_desc));
> +
> + if (++i == tx_ring->total_cnt)
> + i = 0;
> + }
> +
> + tx_ring->tail = i;
> +
> + if (unlikely(netif_queue_stopped(ndev) &&
> + emac_tx_avail(priv) > tx_ring->total_cnt / 4))
> + netif_wake_queue(ndev);
> +
> + netif_tx_unlock(ndev);
> +
> + return 0;
> +}
> +
> +static u32 rx_frame_len(struct emac_desc *desc)
> +{
> + return (desc->desc0 & RX_DESC_0_FRAME_PACKET_LENGTH_MASK) >>
> + RX_DESC_0_FRAME_PACKET_LENGTH_SHIFT;
> +}
> +
> +static int emac_rx_frame_status(struct emac_priv *priv, struct emac_desc *desc)
> +{
> + const char *msg = NULL;
> + int ret = RX_FRAME_OK;
> +
> + /* Drop if not last descriptor, should not normally happen */
> + if (!(desc->desc0 & RX_DESC_0_LAST_DESCRIPTOR)) {
> + msg = "Not last descriptor";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (desc->desc0 & RX_DESC_0_FRAME_RUNT) {
> + msg = "Runt frame";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (desc->desc0 & RX_DESC_0_FRAME_CRC_ERR) {
> + msg = "Frame CRC error";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (desc->desc0 & RX_DESC_0_FRAME_MAX_LEN_ERR) {
> + msg = "Frame exceeds max length";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (desc->desc0 & RX_DESC_0_FRAME_JABBER_ERR) {
> + msg = "Frame jabber error";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (desc->desc0 & RX_DESC_0_FRAME_LENGTH_ERR) {
> + msg = "Frame length error";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (rx_frame_len(desc) <= ETH_FCS_LEN ||
> + rx_frame_len(desc) > priv->dma_buf_sz) {
> + msg = "Frame length unacceptable";
> + ret = RX_FRAME_DISCARD;
> + }
> +
> + if (ret != RX_FRAME_OK)
> + dev_dbg_ratelimited(&priv->ndev->dev, "RX dropped: %s", msg);
> +
> + return ret;
> +}
> +
> +/* RX and TX use the same layout for {RX,TX}_DESC_1_BUFFER_SIZE_{1,2} */
> +
> +static u32 make_buf_size_1(u32 size)
> +{
> + return (size << TX_DESC_1_BUFFER_SIZE_1_SHIFT) &
> + TX_DESC_1_BUFFER_SIZE_1_MASK;
> +}
> +
> +static u32 make_buf_size_2(u32 size)
> +{
> + return (size << TX_DESC_1_BUFFER_SIZE_2_SHIFT) &
> + TX_DESC_1_BUFFER_SIZE_2_MASK;
> +}
> +
> +static void emac_alloc_rx_desc_buffers(struct emac_priv *priv)
> +{
> + struct emac_desc_ring *rx_ring = &priv->rx_ring;
> + struct emac_desc rx_desc, *rx_desc_addr;
> + struct net_device *ndev = priv->ndev;
> + struct emac_rx_desc_buffer *rx_buf;
> + struct sk_buff *skb;
> + u32 i;
> +
> + i = rx_ring->head;
> + rx_buf = &rx_ring->rx_desc_buf[i];
> +
> + while (!rx_buf->skb) {
> + skb = netdev_alloc_skb_ip_align(ndev, priv->dma_buf_sz);
> + if (!skb)
> + break;
> +
> + skb->dev = ndev;
> +
> + rx_buf->skb = skb;
> + rx_buf->dma_len = priv->dma_buf_sz;
> + rx_buf->dma_addr = dma_map_single(&priv->pdev->dev, skb->data,
> + priv->dma_buf_sz,
> + DMA_FROM_DEVICE);
> + if (dma_mapping_error(&priv->pdev->dev, rx_buf->dma_addr)) {
> + dev_err_ratelimited(&ndev->dev, "Mapping skb failed\n");
> + goto err_free_skb;
> + }
> +
> + rx_desc_addr = &((struct emac_desc *)rx_ring->desc_addr)[i];
> +
> + memset(&rx_desc, 0, sizeof(rx_desc));
> +
> + rx_desc.buffer_addr_1 = rx_buf->dma_addr;
> + rx_desc.desc1 = make_buf_size_1(rx_buf->dma_len);
> +
> + if (++i == rx_ring->total_cnt) {
> + rx_desc.desc1 |= RX_DESC_1_END_RING;
> + i = 0;
> + }
> +
> + *rx_desc_addr = rx_desc;
> + dma_wmb();
> + WRITE_ONCE(rx_desc_addr->desc0, rx_desc.desc0 | RX_DESC_0_OWN);
> +
> + rx_buf = &rx_ring->rx_desc_buf[i];
> + }
> +
> + rx_ring->head = i;
> + return;
> +
> +err_free_skb:
> + dev_kfree_skb_any(skb);
> + rx_buf->skb = NULL;
> +}
> +
> +/* Returns number of packets received */
> +static int emac_rx_clean_desc(struct emac_priv *priv, int budget)
> +{
> + struct net_device *ndev = priv->ndev;
> + struct emac_rx_desc_buffer *rx_buf;
> + struct emac_desc_ring *rx_ring;
> + struct sk_buff *skb = NULL;
> + struct emac_desc *rx_desc;
> + u32 got = 0, skb_len, i;
> + int status;
> +
> + rx_ring = &priv->rx_ring;
> +
> + i = rx_ring->tail;
> +
> + while (budget--) {
> + rx_desc = &((struct emac_desc *)rx_ring->desc_addr)[i];
> +
> + /* Stop checking if rx_desc still owned by DMA */
> + if (READ_ONCE(rx_desc->desc0) & RX_DESC_0_OWN)
> + break;
> +
> + dma_rmb();
> +
> + rx_buf = &rx_ring->rx_desc_buf[i];
> +
> + if (!rx_buf->skb)
> + break;
> +
> + got++;
> +
> + dma_unmap_single(&priv->pdev->dev, rx_buf->dma_addr,
> + rx_buf->dma_len, DMA_FROM_DEVICE);
> +
> + status = emac_rx_frame_status(priv, rx_desc);
> + if (unlikely(status == RX_FRAME_DISCARD)) {
> + ndev->stats.rx_dropped++;
> + dev_kfree_skb_irq(rx_buf->skb);
> + rx_buf->skb = NULL;
> + } else {
> + skb = rx_buf->skb;
> + skb_len = rx_frame_len(rx_desc) - ETH_FCS_LEN;
> + skb_put(skb, skb_len);
> + skb->dev = ndev;
> + ndev->hard_header_len = ETH_HLEN;
> +
> + skb->protocol = eth_type_trans(skb, ndev);
> +
> + skb->ip_summed = CHECKSUM_NONE;
> +
> + napi_gro_receive(&priv->napi, skb);
> +
> + ndev->stats.rx_packets++;
> + ndev->stats.rx_bytes += skb_len;
> +
> + memset(rx_desc, 0, sizeof(struct emac_desc));
> + rx_buf->skb = NULL;
> + }
> +
> + if (++i == rx_ring->total_cnt)
> + i = 0;
> + }
> +
> + rx_ring->tail = i;
> +
> + emac_alloc_rx_desc_buffers(priv);
> +
> + return got;
> +}
> +
> +static int emac_rx_poll(struct napi_struct *napi, int budget)
> +{
> + struct emac_priv *priv = container_of(napi, struct emac_priv, napi);
> + int work_done;
> +
> + emac_tx_clean_desc(priv);
> +
> + work_done = emac_rx_clean_desc(priv, budget);
> + if (work_done < budget && napi_complete_done(napi, work_done))
> + emac_enable_interrupt(priv);
> +
> + return work_done;
> +}
> +
> +/*
> + * For convenience, skb->data is fragment 0, frags[0] is fragment 1, etc.
> + *
> + * Each descriptor can hold up to two fragments, called buffer 1 and 2. For each
> + * fragment f, if f % 2 == 0, it uses buffer 1, otherwise it uses buffer 2.
> + */
> +
> +static int emac_tx_map_frag(struct device *dev, struct emac_desc *tx_desc,
> + struct emac_tx_desc_buffer *tx_buf,
> + struct sk_buff *skb, u32 frag_idx)
> +{
> + bool map_as_page, buf_idx;
> + const skb_frag_t *frag;
> + phys_addr_t addr;
> + u32 len;
> + int ret;
> +
> + buf_idx = frag_idx % 2;
> +
> + if (frag_idx == 0) {
> + /* Non-fragmented part */
> + len = skb_headlen(skb);
> + addr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE);
> + map_as_page = false;
> + } else {
> + /* Fragment */
> + frag = &skb_shinfo(skb)->frags[frag_idx - 1];
> + len = skb_frag_size(frag);
> + addr = skb_frag_dma_map(dev, frag, 0, len, DMA_TO_DEVICE);
> + map_as_page = true;
> + }
> +
> + ret = dma_mapping_error(dev, addr);
> + if (ret)
> + return ret;
> +
> + tx_buf->buf[buf_idx].dma_addr = addr;
> + tx_buf->buf[buf_idx].dma_len = len;
> + tx_buf->buf[buf_idx].map_as_page = map_as_page;
> +
> + if (buf_idx == 0) {
> + tx_desc->buffer_addr_1 = addr;
> + tx_desc->desc1 |= make_buf_size_1(len);
> + } else {
> + tx_desc->buffer_addr_2 = addr;
> + tx_desc->desc1 |= make_buf_size_2(len);
> + }
> +
> + return 0;
> +}
> +
> +static void emac_tx_mem_map(struct emac_priv *priv, struct sk_buff *skb)
> +{
> + struct emac_desc_ring *tx_ring = &priv->tx_ring;
> + struct emac_desc tx_desc, *tx_desc_addr;
> + struct device *dev = &priv->pdev->dev;
> + struct emac_tx_desc_buffer *tx_buf;
> + u32 head, old_head, frag_num, f;
> + bool buf_idx;
> +
> + frag_num = skb_shinfo(skb)->nr_frags;
> + head = tx_ring->head;
> + old_head = head;
> +
> + for (f = 0; f < frag_num + 1; f++) {
> + buf_idx = f % 2;
> +
> + /*
> + * If using buffer 1, initialize a new desc. Otherwise, use
> + * buffer 2 of previous fragment's desc.
> + */
> + if (!buf_idx) {
> + tx_buf = &tx_ring->tx_desc_buf[head];
> + tx_desc_addr =
> + &((struct emac_desc *)tx_ring->desc_addr)[head];
> + memset(&tx_desc, 0, sizeof(tx_desc));
> +
> + /*
> + * Give ownership for all but first desc initially. For
> + * first desc, give at the end so DMA cannot start
> + * reading uninitialized descs.
> + */
> + if (head != old_head)
> + tx_desc.desc0 |= TX_DESC_0_OWN;
> +
> + if (++head == tx_ring->total_cnt) {
> + /* Just used last desc in ring */
> + tx_desc.desc1 |= TX_DESC_1_END_RING;
> + head = 0;
> + }
> + }
> +
> + if (emac_tx_map_frag(dev, &tx_desc, tx_buf, skb, f)) {
> + dev_err_ratelimited(&priv->ndev->dev,
> + "Map TX frag %d failed\n", f);
> + goto err_free_skb;
> + }
> +
> + if (f == 0)
> + tx_desc.desc1 |= TX_DESC_1_FIRST_SEGMENT;
> +
> + if (f == frag_num) {
> + tx_desc.desc1 |= TX_DESC_1_LAST_SEGMENT;
> + tx_buf->skb = skb;
> + if (emac_tx_should_interrupt(priv, frag_num + 1))
> + tx_desc.desc1 |=
> + TX_DESC_1_INTERRUPT_ON_COMPLETION;
> + }
> +
> + *tx_desc_addr = tx_desc;
> + }
> +
> + /* All descriptors are ready, give ownership for first desc */
> + tx_desc_addr = &((struct emac_desc *)tx_ring->desc_addr)[old_head];
> + dma_wmb();
> + WRITE_ONCE(tx_desc_addr->desc0, tx_desc_addr->desc0 | TX_DESC_0_OWN);
> +
> + emac_dma_start_transmit(priv);
> +
> + tx_ring->head = head;
> +
> + priv->ndev->stats.tx_packets++;
> + priv->ndev->stats.tx_bytes += skb->len;
> +
> + return;
> +
> +err_free_skb:
> + dev_kfree_skb_any(skb);
> + priv->ndev->stats.tx_dropped++;
> +}
> +
> +static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + int nfrags = skb_shinfo(skb)->nr_frags;
> + struct device *dev = &priv->pdev->dev;
> +
> + if (unlikely(emac_tx_avail(priv) < nfrags + 1)) {
> + if (!netif_queue_stopped(ndev)) {
> + netif_stop_queue(ndev);
> + dev_err_ratelimited(dev, "TX ring full, stop TX queue\n");
> + }
> + return NETDEV_TX_BUSY;
> + }
> +
> + emac_tx_mem_map(priv, skb);
> +
> + /* Make sure there is space in the ring for the next TX. */
> + if (unlikely(emac_tx_avail(priv) <= MAX_SKB_FRAGS + 2))
> + netif_stop_queue(ndev);
> +
> + return NETDEV_TX_OK;
> +}
> +
> +static int emac_set_mac_address(struct net_device *ndev, void *addr)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + int ret = eth_mac_addr(ndev, addr);
> +
> + if (ret)
> + return ret;
> +
> + /* If running, set now; if not running it will be set in emac_up. */
> + if (netif_running(ndev))
> + emac_set_mac_addr(priv, ndev->dev_addr);
> +
> + return 0;
> +}
> +
> +static void emac_mac_multicast_filter_clear(struct emac_priv *priv)
> +{
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, 0x0);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, 0x0);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, 0x0);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, 0x0);
> +}
> +
> +/* Configure Multicast and Promiscuous modes */
> +static void emac_set_rx_mode(struct net_device *ndev)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + u32 crc32, bit, reg, hash, val;
> + struct netdev_hw_addr *ha;
> + u32 mc_filter[4] = { 0 };
> +
> + val = emac_rd(priv, MAC_ADDRESS_CONTROL);
> +
> + val &= ~MREGBIT_PROMISCUOUS_MODE;
> +
> + if (ndev->flags & IFF_PROMISC) {
> + /* Enable promisc mode */
> + val |= MREGBIT_PROMISCUOUS_MODE;
> + } else if ((ndev->flags & IFF_ALLMULTI) ||
> + (netdev_mc_count(ndev) > HASH_TABLE_SIZE)) {
> + /* Accept all multicast frames by setting every bit */
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, 0xffff);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, 0xffff);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, 0xffff);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, 0xffff);
> + } else if (!netdev_mc_empty(ndev)) {
> + emac_mac_multicast_filter_clear(priv);
> + netdev_for_each_mc_addr(ha, ndev) {
> + /* Calculate the CRC of the MAC address */
> + crc32 = ether_crc(ETH_ALEN, ha->addr);
> +
> + /*
> + * The hash table is an array of 4 16-bit registers. It
> + * is treated like an array of 64 bits (bits[hash]). Use
> + * the upper 6 bits of the above CRC as the hash value.
> + */
> + hash = (crc32 >> 26) & 0x3F;
> + reg = hash / 16;
> + bit = hash % 16;
> + mc_filter[reg] |= BIT(bit);
> + }
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, mc_filter[0]);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, mc_filter[1]);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, mc_filter[2]);
> + emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, mc_filter[3]);
> + }
> +
> + emac_wr(priv, MAC_ADDRESS_CONTROL, val);
> +}
> +
> +static int emac_change_mtu(struct net_device *ndev, int mtu)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + u32 frame_len;
> +
> + if (netif_running(ndev)) {
> + netdev_err(ndev, "must be stopped to change MTU\n");
> + return -EBUSY;
> + }
> +
> + frame_len = mtu + ETH_HLEN + ETH_FCS_LEN;
> +
> + if (frame_len <= EMAC_DEFAULT_BUFSIZE)
> + priv->dma_buf_sz = EMAC_DEFAULT_BUFSIZE;
> + else if (frame_len <= EMAC_RX_BUF_2K)
> + priv->dma_buf_sz = EMAC_RX_BUF_2K;
> + else
> + priv->dma_buf_sz = EMAC_RX_BUF_4K;
> +
> + ndev->mtu = mtu;
> +
> + return 0;
> +}
> +
> +static void emac_tx_timeout(struct net_device *ndev, unsigned int txqueue)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> +
> + schedule_work(&priv->tx_timeout_task);
> +}
> +
> +static int emac_mii_read(struct mii_bus *bus, int phy_addr, int regnum)
> +{
> + struct emac_priv *priv = bus->priv;
> + u32 cmd = 0, val;
> + int ret;
> +
> + cmd |= phy_addr & 0x1F;
> + cmd |= (regnum & 0x1F) << 5;
> + cmd |= MREGBIT_START_MDIO_TRANS | MREGBIT_MDIO_READ_WRITE;
> +
> + emac_wr(priv, MAC_MDIO_DATA, 0x0);
> + emac_wr(priv, MAC_MDIO_CONTROL, cmd);
> +
> + ret = readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
> + !((val >> 15) & 0x1), 100, 10000);
> +
> + if (ret)
> + return ret;
> +
> + val = emac_rd(priv, MAC_MDIO_DATA);
> + return val;
> +}
> +
> +static int emac_mii_write(struct mii_bus *bus, int phy_addr, int regnum,
> + u16 value)
> +{
> + struct emac_priv *priv = bus->priv;
> + u32 cmd = 0, val;
> + int ret;
> +
> + emac_wr(priv, MAC_MDIO_DATA, value);
> +
> + cmd |= phy_addr & 0x1F;
> + cmd |= (regnum & 0x1F) << 5;
> + cmd |= MREGBIT_START_MDIO_TRANS;
> +
> + emac_wr(priv, MAC_MDIO_CONTROL, cmd);
> +
> + ret = readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
> + !((val >> 15) & 0x1), 100, 10000);
> +
> + return ret;
> +}
> +
> +static int emac_mdio_init(struct emac_priv *priv)
> +{
> + struct device *dev = &priv->pdev->dev;
> + struct device_node *mii_np;
> + struct mii_bus *mii;
> + int ret;
> +
> + mii = devm_mdiobus_alloc(dev);
> + if (!mii)
> + return -ENOMEM;
> +
> + mii->priv = priv;
> + mii->name = "k1_emac_mii";
> + mii->read = emac_mii_read;
> + mii->write = emac_mii_write;
> + mii->parent = dev;
> + mii->phy_mask = 0xffffffff;
> + snprintf(mii->id, MII_BUS_ID_SIZE, "%s", priv->pdev->name);
> +
> + mii_np = of_get_available_child_by_name(dev->of_node, "mdio-bus");
> +
> + ret = devm_of_mdiobus_register(dev, mii, mii_np);
> + if (ret)
> + dev_err_probe(dev, ret, "Failed to register mdio bus\n");
> +
> + of_node_put(mii_np);
> + return ret;
> +}
> +
> +static void emac_set_tx_fc(struct emac_priv *priv, bool enable)
> +{
> + u32 val;
> +
> + val = emac_rd(priv, MAC_FC_CONTROL);
> +
> + if (enable) {
> + val |= MREGBIT_FC_GENERATION_ENABLE;
> + val |= MREGBIT_AUTO_FC_GENERATION_ENABLE;
> + } else {
> + val &= ~MREGBIT_FC_GENERATION_ENABLE;
> + val &= ~MREGBIT_AUTO_FC_GENERATION_ENABLE;
> + }
> +
> + emac_wr(priv, MAC_FC_CONTROL, val);
> +}
> +
> +static void emac_set_rx_fc(struct emac_priv *priv, bool enable)
> +{
> + u32 val = emac_rd(priv, MAC_FC_CONTROL);
> +
> + if (enable)
> + val |= MREGBIT_FC_DECODE_ENABLE;
> + else
> + val &= ~MREGBIT_FC_DECODE_ENABLE;
> +
> + emac_wr(priv, MAC_FC_CONTROL, val);
> +}
> +
> +static void emac_set_fc(struct emac_priv *priv, u8 fc)
> +{
> + emac_set_tx_fc(priv, fc & FLOW_CTRL_TX);
> + emac_set_rx_fc(priv, fc & FLOW_CTRL_RX);
> + priv->flow_control = fc;
> +}
> +
> +static void emac_set_fc_autoneg(struct emac_priv *priv)
> +{
> + struct phy_device *phydev = priv->ndev->phydev;
> + u32 local_adv, remote_adv;
> + u8 fc;
> +
> + local_adv = linkmode_adv_to_lcl_adv_t(phydev->advertising);
> +
> + remote_adv = 0;
> +
> + if (phydev->pause)
> + remote_adv |= LPA_PAUSE_CAP;
> +
> + if (phydev->asym_pause)
> + remote_adv |= LPA_PAUSE_ASYM;
> +
> + fc = mii_resolve_flowctrl_fdx(local_adv, remote_adv);
> +
> + priv->flow_control_autoneg = true;
> +
> + emac_set_fc(priv, fc);
> +}
> +
> +/*
> + * Even though this MAC supports gigabit operation, it only provides 32-bit
> + * statistics counters. The most overflow-prone counters are the "bytes" ones,
> + * which at gigabit overflow about twice a minute.
> + *
> + * Therefore, we maintain the high 32 bits of counters ourselves, incrementing
> + * every time statistics seem to go backwards. Also, update periodically to
> + * catch overflows when we are not otherwise checking the statistics often
> + * enough.
> + */
> +
> +#define EMAC_STATS_TIMER_PERIOD 20
> +
> +static int emac_read_stat_cnt(struct emac_priv *priv, u8 cnt, u32 *res,
> + u32 control_reg, u32 high_reg, u32 low_reg)
> +{
> + u32 val;
> + int ret;
> +
> + /* The "read" bit is the same for TX and RX */
> +
> + val = MREGBIT_START_TX_COUNTER_READ | cnt;
> + emac_wr(priv, control_reg, val);
> + val = emac_rd(priv, control_reg);
> +
> + ret = readl_poll_timeout_atomic(priv->iobase + control_reg, val,
> + !(val & MREGBIT_START_TX_COUNTER_READ),
> + 100, 10000);
> +
> + if (ret) {
> + netdev_err(priv->ndev, "Read stat timeout\n");
> + return ret;
> + }
> +
> + *res = emac_rd(priv, high_reg) << 16;
> + *res |= (u16)emac_rd(priv, low_reg);
> +
> + return 0;
> +}
> +
> +static int emac_tx_read_stat_cnt(struct emac_priv *priv, u8 cnt, u32 *res)
> +{
> + return emac_read_stat_cnt(priv, cnt, res, MAC_TX_STATCTR_CONTROL,
> + MAC_TX_STATCTR_DATA_HIGH,
> + MAC_TX_STATCTR_DATA_LOW);
> +}
> +
> +static int emac_rx_read_stat_cnt(struct emac_priv *priv, u8 cnt, u32 *res)
> +{
> + return emac_read_stat_cnt(priv, cnt, res, MAC_RX_STATCTR_CONTROL,
> + MAC_RX_STATCTR_DATA_HIGH,
> + MAC_RX_STATCTR_DATA_LOW);
> +}
> +
> +static void emac_update_counter(u64 *counter, u32 new_low)
> +{
> + u32 old_low = (u32)*counter;
> + u64 high = *counter >> 32;
> +
> + if (old_low > new_low) {
> + /* Overflowed, increment high 32 bits */
> + high++;
> + }
> +
> + *counter = (high << 32) | new_low;
> +}
> +
> +static void emac_stats_update(struct emac_priv *priv)
> +{
> + u64 *tx_stats_off = (u64 *)&priv->tx_stats_off;
> + u64 *rx_stats_off = (u64 *)&priv->rx_stats_off;
> + u64 *tx_stats = (u64 *)&priv->tx_stats;
> + u64 *rx_stats = (u64 *)&priv->rx_stats;
> + u32 i, res;
> +
> + assert_spin_locked(&priv->stats_lock);
> +
> + if (!netif_running(priv->ndev) || !netif_device_present(priv->ndev)) {
> + /* Not up, don't try to update */
> + return;
> + }
> +
> + for (i = 0; i < sizeof(priv->tx_stats) / sizeof(*tx_stats); i++) {
> + /*
> + * If reading stats times out, everything is broken and there's
> + * nothing we can do. Reading statistics also can't return an
> + * error, so just return without updating and without
> + * rescheduling.
> + */
> + if (emac_tx_read_stat_cnt(priv, i, &res))
> + return;
> +
> + /*
> + * Re-initializing while bringing interface up resets counters
> + * to zero, so to provide continuity, we add the values saved
> + * last time we did emac_down() to the new hardware-provided
> + * value.
> + */
> + emac_update_counter(&tx_stats[i], res + (u32)tx_stats_off[i]);
> + }
> +
> + /* Similar remarks as TX stats */
> + for (i = 0; i < sizeof(priv->rx_stats) / sizeof(*rx_stats); i++) {
> + if (emac_rx_read_stat_cnt(priv, i, &res))
> + return;
> + emac_update_counter(&rx_stats[i], res + (u32)rx_stats_off[i]);
> + }
> +
> + mod_timer(&priv->stats_timer, jiffies + EMAC_STATS_TIMER_PERIOD * HZ);
> +}
> +
> +static void emac_stats_timer(struct timer_list *t)
> +{
> + struct emac_priv *priv = timer_container_of(priv, t, stats_timer);
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +static const struct ethtool_rmon_hist_range emac_rmon_hist_ranges[] = {
> + { 64, 64 },
> + { 65, 127 },
> + { 128, 255 },
> + { 256, 511 },
> + { 512, 1023 },
> + { 1024, 1518 },
> + { 1519, 4096 },
> + { /* sentinel */ },
> +};
> +
> +static void emac_get_stats64(struct net_device *dev,
> + struct rtnl_link_stats64 *storage)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + struct emac_hw_tx_stats *tx_stats;
> + struct emac_hw_rx_stats *rx_stats;
> +
> + tx_stats = &priv->tx_stats;
> + rx_stats = &priv->rx_stats;
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + storage->tx_packets = tx_stats->tx_ok_pkts;
> + storage->tx_bytes = tx_stats->tx_ok_bytes;
> + storage->tx_errors = tx_stats->tx_err_pkts;
> +
> + storage->rx_packets = rx_stats->rx_ok_pkts;
> + storage->rx_bytes = rx_stats->rx_ok_bytes;
> + storage->rx_errors = rx_stats->rx_err_total_pkts;
> + storage->rx_crc_errors = rx_stats->rx_crc_err_pkts;
> + storage->rx_frame_errors = rx_stats->rx_align_err_pkts;
> + storage->rx_length_errors = rx_stats->rx_len_err_pkts;
> +
> + storage->collisions = tx_stats->tx_singleclsn_pkts;
> + storage->collisions += tx_stats->tx_multiclsn_pkts;
> + storage->collisions += tx_stats->tx_excessclsn_pkts;
> +
> + storage->rx_missed_errors = rx_stats->rx_drp_fifo_full_pkts;
> + storage->rx_missed_errors += rx_stats->rx_truncate_fifo_full_pkts;
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +static void emac_get_rmon_stats(struct net_device *dev,
> + struct ethtool_rmon_stats *rmon_stats,
> + const struct ethtool_rmon_hist_range **ranges)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + struct emac_hw_rx_stats *rx_stats;
> +
> + rx_stats = &priv->rx_stats;
> +
> + *ranges = emac_rmon_hist_ranges;
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + rmon_stats->undersize_pkts = rx_stats->rx_len_undersize_pkts;
> + rmon_stats->oversize_pkts = rx_stats->rx_len_oversize_pkts;
> + rmon_stats->fragments = rx_stats->rx_len_fragment_pkts;
> + rmon_stats->jabbers = rx_stats->rx_len_jabber_pkts;
> +
> + /* Only RX has histogram stats */
> +
> + rmon_stats->hist[0] = rx_stats->rx_64_pkts;
> + rmon_stats->hist[1] = rx_stats->rx_65_127_pkts;
> + rmon_stats->hist[2] = rx_stats->rx_128_255_pkts;
> + rmon_stats->hist[3] = rx_stats->rx_256_511_pkts;
> + rmon_stats->hist[4] = rx_stats->rx_512_1023_pkts;
> + rmon_stats->hist[5] = rx_stats->rx_1024_1518_pkts;
> + rmon_stats->hist[6] = rx_stats->rx_1519_plus_pkts;
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +static void emac_get_eth_mac_stats(struct net_device *dev,
> + struct ethtool_eth_mac_stats *mac_stats)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + struct emac_hw_tx_stats *tx_stats;
> + struct emac_hw_rx_stats *rx_stats;
> +
> + tx_stats = &priv->tx_stats;
> + rx_stats = &priv->rx_stats;
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + mac_stats->MulticastFramesXmittedOK = tx_stats->tx_multicast_pkts;
> + mac_stats->BroadcastFramesXmittedOK = tx_stats->tx_broadcast_pkts;
> +
> + mac_stats->MulticastFramesReceivedOK = rx_stats->rx_multicast_pkts;
> + mac_stats->BroadcastFramesReceivedOK = rx_stats->rx_broadcast_pkts;
> +
> + mac_stats->SingleCollisionFrames = tx_stats->tx_singleclsn_pkts;
> + mac_stats->MultipleCollisionFrames = tx_stats->tx_multiclsn_pkts;
> + mac_stats->LateCollisions = tx_stats->tx_lateclsn_pkts;
> + mac_stats->FramesAbortedDueToXSColls = tx_stats->tx_excessclsn_pkts;
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +static void emac_get_pause_stats(struct net_device *dev,
> + struct ethtool_pause_stats *pause_stats)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + struct emac_hw_tx_stats *tx_stats;
> + struct emac_hw_rx_stats *rx_stats;
> +
> + tx_stats = &priv->tx_stats;
> + rx_stats = &priv->rx_stats;
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + pause_stats->tx_pause_frames = tx_stats->tx_pause_pkts;
> + pause_stats->rx_pause_frames = rx_stats->rx_pause_pkts;
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +/* Other statistics that are not derivable from standard statistics */
> +
> +#define EMAC_ETHTOOL_STAT(type, name) \
> + { offsetof(type, name) / sizeof(u64), #name }
> +
> +static const struct emac_ethtool_stats {
> + size_t offset;
> + char str[ETH_GSTRING_LEN];
> +} emac_ethtool_rx_stats[] = {
> + EMAC_ETHTOOL_STAT(struct emac_hw_rx_stats, rx_drp_fifo_full_pkts),
> + EMAC_ETHTOOL_STAT(struct emac_hw_rx_stats, rx_truncate_fifo_full_pkts),
> +};
> +
> +static int emac_get_sset_count(struct net_device *dev, int sset)
> +{
> + switch (sset) {
> + case ETH_SS_STATS:
> + return ARRAY_SIZE(emac_ethtool_rx_stats);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static void emac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
> +{
> + int i;
> +
> + switch (stringset) {
> + case ETH_SS_STATS:
> + for (i = 0; i < ARRAY_SIZE(emac_ethtool_rx_stats); i++) {
> + memcpy(data, emac_ethtool_rx_stats[i].str,
> + ETH_GSTRING_LEN);
> + data += ETH_GSTRING_LEN;
> + }
> + break;
> + }
> +}
> +
> +static void emac_get_ethtool_stats(struct net_device *dev,
> + struct ethtool_stats *stats, u64 *data)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + u64 *rx_stats = (u64 *)&priv->rx_stats;
> + int i;
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + for (i = 0; i < ARRAY_SIZE(emac_ethtool_rx_stats); i++)
> + data[i] = rx_stats[emac_ethtool_rx_stats[i].offset];
> +
> + spin_unlock(&priv->stats_lock);
> +}
> +
> +static int emac_ethtool_get_regs_len(struct net_device *dev)
> +{
> + return (EMAC_DMA_REG_CNT + EMAC_MAC_REG_CNT) * sizeof(u32);
> +}
> +
> +static void emac_ethtool_get_regs(struct net_device *dev,
> + struct ethtool_regs *regs, void *space)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + u32 *reg_space = space;
> + int i;
> +
> + regs->version = 1;
> +
> + for (i = 0; i < EMAC_DMA_REG_CNT; i++)
> + reg_space[i] = emac_rd(priv, DMA_CONFIGURATION + i * 4);
> +
> + for (i = 0; i < EMAC_MAC_REG_CNT; i++)
> + reg_space[i + EMAC_DMA_REG_CNT] =
> + emac_rd(priv, MAC_GLOBAL_CONTROL + i * 4);
> +}
> +
> +static void emac_get_pauseparam(struct net_device *dev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> +
> + pause->autoneg = priv->flow_control_autoneg;
> + pause->tx_pause = !!(priv->flow_control & FLOW_CTRL_TX);
> + pause->rx_pause = !!(priv->flow_control & FLOW_CTRL_RX);
> +}
> +
> +static int emac_set_pauseparam(struct net_device *dev,
> + struct ethtool_pauseparam *pause)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + u8 fc = 0;
> +
> + priv->flow_control_autoneg = pause->autoneg;
> +
> + if (pause->autoneg) {
> + emac_set_fc_autoneg(priv);
> + } else {
> + if (pause->tx_pause)
> + fc |= FLOW_CTRL_TX;
> +
> + if (pause->rx_pause)
> + fc |= FLOW_CTRL_RX;
> +
> + emac_set_fc(priv, fc);
> + }
> +
> + return 0;
> +}
> +
> +static void emac_get_drvinfo(struct net_device *dev,
> + struct ethtool_drvinfo *info)
> +{
> + strscpy(info->driver, DRIVER_NAME, sizeof(info->driver));
> + info->n_stats = ARRAY_SIZE(emac_ethtool_rx_stats);
> +}
> +
> +static void emac_tx_timeout_task(struct work_struct *work)
> +{
> + struct net_device *ndev;
> + struct emac_priv *priv;
> +
> + priv = container_of(work, struct emac_priv, tx_timeout_task);
> + ndev = priv->ndev;
> +
> + rtnl_lock();
> +
> + /* No need to reset if already down */
> + if (!netif_running(ndev)) {
> + rtnl_unlock();
> + return;
> + }
> +
> + netdev_err(ndev, "MAC reset due to TX timeout\n");
> +
> + netif_trans_update(ndev); /* prevent tx timeout */
> + dev_close(ndev);
> + dev_open(ndev, NULL);
> +
> + rtnl_unlock();
> +}
> +
> +static void emac_sw_init(struct emac_priv *priv)
> +{
> + priv->dma_buf_sz = EMAC_DEFAULT_BUFSIZE;
> +
> + priv->tx_ring.total_cnt = DEFAULT_TX_RING_NUM;
> + priv->rx_ring.total_cnt = DEFAULT_RX_RING_NUM;
> +
> + spin_lock_init(&priv->stats_lock);
> +
> + INIT_WORK(&priv->tx_timeout_task, emac_tx_timeout_task);
> +
> + priv->tx_coal_frames = EMAC_TX_FRAMES;
> + priv->tx_coal_timeout = EMAC_TX_COAL_TIMEOUT;
> +
> + timer_setup(&priv->txtimer, emac_tx_coal_timer, 0);
> + timer_setup(&priv->stats_timer, emac_stats_timer, 0);
> +}
> +
> +static irqreturn_t emac_interrupt_handler(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *)dev_id;
> + struct emac_priv *priv = netdev_priv(ndev);
> + bool should_schedule = false;
> + u32 clr = 0;
> + u32 status;
> +
> + status = emac_rd(priv, DMA_STATUS_IRQ);
> +
> + if (status & MREGBIT_TRANSMIT_TRANSFER_DONE_IRQ) {
> + clr |= MREGBIT_TRANSMIT_TRANSFER_DONE_IRQ;
> + should_schedule = true;
> + }
> +
> + if (status & MREGBIT_TRANSMIT_DES_UNAVAILABLE_IRQ)
> + clr |= MREGBIT_TRANSMIT_DES_UNAVAILABLE_IRQ;
> +
> + if (status & MREGBIT_TRANSMIT_DMA_STOPPED_IRQ)
> + clr |= MREGBIT_TRANSMIT_DMA_STOPPED_IRQ;
> +
> + if (status & MREGBIT_RECEIVE_TRANSFER_DONE_IRQ) {
> + clr |= MREGBIT_RECEIVE_TRANSFER_DONE_IRQ;
> + should_schedule = true;
> + }
> +
> + if (status & MREGBIT_RECEIVE_DES_UNAVAILABLE_IRQ)
> + clr |= MREGBIT_RECEIVE_DES_UNAVAILABLE_IRQ;
> +
> + if (status & MREGBIT_RECEIVE_DMA_STOPPED_IRQ)
> + clr |= MREGBIT_RECEIVE_DMA_STOPPED_IRQ;
> +
> + if (status & MREGBIT_RECEIVE_MISSED_FRAME_IRQ)
> + clr |= MREGBIT_RECEIVE_MISSED_FRAME_IRQ;
> +
> + if (should_schedule) {
> + if (napi_schedule_prep(&priv->napi)) {
> + emac_disable_interrupt(priv);
> + __napi_schedule_irqoff(&priv->napi);
> + }
> + }
> +
> + emac_wr(priv, DMA_STATUS_IRQ, clr);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void emac_configure_tx(struct emac_priv *priv)
> +{
> + u32 val;
> +
> + /* Set base address */
> + val = (u32)(priv->tx_ring.desc_dma_addr);
> + emac_wr(priv, DMA_TRANSMIT_BASE_ADDRESS, val);
> +
> + /* Set TX inter-frame gap value, enable transmit */
> + val = emac_rd(priv, MAC_TRANSMIT_CONTROL);
> + val &= ~MREGBIT_IFG_LEN;
> + val |= MREGBIT_TRANSMIT_ENABLE;
> + val |= MREGBIT_TRANSMIT_AUTO_RETRY;
> + emac_wr(priv, MAC_TRANSMIT_CONTROL, val);
> +
> + emac_wr(priv, DMA_TRANSMIT_AUTO_POLL_COUNTER, 0x0);
> +
> + /* Start TX DMA */
> + val = emac_rd(priv, DMA_CONTROL);
> + val |= MREGBIT_START_STOP_TRANSMIT_DMA;
> + emac_wr(priv, DMA_CONTROL, val);
> +}
> +
> +static void emac_configure_rx(struct emac_priv *priv)
> +{
> + u32 val;
> +
> + /* Set base address */
> + val = (u32)(priv->rx_ring.desc_dma_addr);
> + emac_wr(priv, DMA_RECEIVE_BASE_ADDRESS, val);
> +
> + /* Enable receive */
> + val = emac_rd(priv, MAC_RECEIVE_CONTROL);
> + val |= MREGBIT_RECEIVE_ENABLE;
> + val |= MREGBIT_STORE_FORWARD;
> + emac_wr(priv, MAC_RECEIVE_CONTROL, val);
> +
> + /* Start RX DMA */
> + val = emac_rd(priv, DMA_CONTROL);
> + val |= MREGBIT_START_STOP_RECEIVE_DMA;
> + emac_wr(priv, DMA_CONTROL, val);
> +}
> +
> +static void emac_adjust_link(struct net_device *dev)
> +{
> + struct emac_priv *priv = netdev_priv(dev);
> + struct phy_device *phydev = dev->phydev;
> + u32 ctrl;
> +
> + if (phydev->link) {
> + ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
> +
> + /* Update duplex and speed from PHY */
> +
> + if (!phydev->duplex)
> + ctrl &= ~MREGBIT_FULL_DUPLEX_MODE;
> + else
> + ctrl |= MREGBIT_FULL_DUPLEX_MODE;
> +
> + ctrl &= ~MREGBIT_SPEED;
> +
> + switch (phydev->speed) {
> + case SPEED_1000:
> + ctrl |= MREGBIT_SPEED_1000M;
> + break;
> + case SPEED_100:
> + ctrl |= MREGBIT_SPEED_100M;
> + break;
> + case SPEED_10:
> + ctrl |= MREGBIT_SPEED_10M;
> + break;
> + default:
> + netdev_err(dev, "Unknown speed: %d\n", phydev->speed);
> + phydev->speed = SPEED_UNKNOWN;
> + break;
> + }
> +
> + emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
> +
> + emac_set_fc_autoneg(priv);
> + }
> +
> + phy_print_status(phydev);
> +}
> +
> +static void emac_update_delay_line(struct emac_priv *priv)
> +{
> + u32 mask = 0, val = 0;
> +
> + mask |= EMAC_RX_DLINE_EN;
> + mask |= EMAC_RX_DLINE_STEP_MASK | EMAC_RX_DLINE_CODE_MASK;
> + mask |= EMAC_TX_DLINE_EN;
> + mask |= EMAC_TX_DLINE_STEP_MASK | EMAC_TX_DLINE_CODE_MASK;
> +
> + switch (priv->phy_interface) {
> + case PHY_INTERFACE_MODE_RGMII:
> + case PHY_INTERFACE_MODE_RGMII_ID:
> + case PHY_INTERFACE_MODE_RGMII_RXID:
> + case PHY_INTERFACE_MODE_RGMII_TXID:
> + val |= EMAC_RX_DLINE_EN;
> + val |= EMAC_DLINE_STEP_15P6 << EMAC_RX_DLINE_STEP_SHIFT;
> + val |= (priv->rx_delay << EMAC_RX_DLINE_CODE_SHIFT) &
> + EMAC_RX_DLINE_CODE_MASK;
> +
> + val |= EMAC_TX_DLINE_EN;
> + val |= EMAC_DLINE_STEP_15P6 << EMAC_TX_DLINE_STEP_SHIFT;
> + val |= (priv->tx_delay << EMAC_TX_DLINE_CODE_SHIFT) &
> + EMAC_TX_DLINE_CODE_MASK;
> + break;
> + default:
> + break;
> + }
> +
> + regmap_update_bits(priv->regmap_apmu,
> + priv->regmap_apmu_offset + APMU_EMAC_DLINE_REG,
> + mask, val);
> +}
> +
> +static int emac_phy_connect(struct net_device *ndev)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + struct device *dev = &priv->pdev->dev;
> + struct phy_device *phydev;
> + struct device_node *np;
> + int ret;
> +
> + ret = of_get_phy_mode(dev->of_node, &priv->phy_interface);
> + if (ret) {
> + dev_err(dev, "No phy-mode found");
> + return ret;
> + }
> +
> + np = of_parse_phandle(dev->of_node, "phy-handle", 0);
> + if (!np && of_phy_is_fixed_link(dev->of_node))
> + np = of_node_get(dev->of_node);
> +
> + if (!np) {
> + dev_err(dev, "No PHY specified");
> + return -ENODEV;
> + }
> +
> + ret = emac_phy_interface_config(priv);
> + if (ret)
> + goto err_node_put;
> +
> + phydev = of_phy_connect(ndev, np, &emac_adjust_link, 0,
> + priv->phy_interface);
> + if (!phydev) {
> + dev_err(dev, "Could not attach to PHY\n");
> + ret = -ENODEV;
> + goto err_node_put;
> + }
> +
> + phy_support_asym_pause(phydev);
> +
> + phydev->mac_managed_pm = true;
> +
> + emac_update_delay_line(priv);
> +
> +err_node_put:
> + of_node_put(np);
> + return ret;
> +}
> +
> +static int emac_up(struct emac_priv *priv)
> +{
> + struct platform_device *pdev = priv->pdev;
> + struct net_device *ndev = priv->ndev;
> + int ret;
> +
> + pm_runtime_get_sync(&pdev->dev);
> +
> + ret = emac_phy_connect(ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "emac_phy_connect failed\n");
> + goto err_pm_put;
> + }
> +
> + emac_init_hw(priv);
> +
> + emac_set_mac_addr(priv, ndev->dev_addr);
> + emac_configure_tx(priv);
> + emac_configure_rx(priv);
> +
> + emac_alloc_rx_desc_buffers(priv);
> +
> + phy_start(ndev->phydev);
> +
> + ret = request_irq(priv->irq, emac_interrupt_handler, IRQF_SHARED,
> + ndev->name, ndev);
> + if (ret) {
> + dev_err(&pdev->dev, "request_irq failed\n");
> + goto err_reset_disconnect_phy;
> + }
> +
> + /* Don't enable MAC interrupts */
> + emac_wr(priv, MAC_INTERRUPT_ENABLE, 0x0);
> +
> + /* Enable DMA interrupts */
> + emac_wr(priv, DMA_INTERRUPT_ENABLE,
> + MREGBIT_TRANSMIT_TRANSFER_DONE_INTR_ENABLE |
> + MREGBIT_TRANSMIT_DMA_STOPPED_INTR_ENABLE |
> + MREGBIT_RECEIVE_TRANSFER_DONE_INTR_ENABLE |
> + MREGBIT_RECEIVE_DMA_STOPPED_INTR_ENABLE |
> + MREGBIT_RECEIVE_MISSED_FRAME_INTR_ENABLE);
> +
> + napi_enable(&priv->napi);
> +
> + netif_start_queue(ndev);
> +
> + emac_stats_timer(&priv->stats_timer);
> +
> + return 0;
> +
> +err_reset_disconnect_phy:
> + emac_reset_hw(priv);
> + phy_disconnect(ndev->phydev);
> +
> +err_pm_put:
> + pm_runtime_put_sync(&pdev->dev);
> + return ret;
> +}
> +
> +static int emac_down(struct emac_priv *priv)
> +{
> + struct platform_device *pdev = priv->pdev;
> + struct net_device *ndev = priv->ndev;
> +
> + netif_stop_queue(ndev);
> +
> + phy_disconnect(ndev->phydev);
> +
> + emac_wr(priv, MAC_INTERRUPT_ENABLE, 0x0);
> + emac_wr(priv, DMA_INTERRUPT_ENABLE, 0x0);
> +
> + free_irq(priv->irq, ndev);
> +
> + napi_disable(&priv->napi);
> +
> + timer_delete_sync(&priv->txtimer);
> + cancel_work_sync(&priv->tx_timeout_task);
> +
> + timer_delete_sync(&priv->stats_timer);
> +
> + emac_reset_hw(priv);
> +
> + /* Update and save current stats, see emac_stats_update() for usage */
> +
> + spin_lock(&priv->stats_lock);
> +
> + emac_stats_update(priv);
> +
> + priv->tx_stats_off = priv->tx_stats;
> + priv->rx_stats_off = priv->rx_stats;
> +
> + spin_unlock(&priv->stats_lock);
> +
> + pm_runtime_put_sync(&pdev->dev);
> + return 0;
> +}
> +
> +/* Called when net interface is brought up. */
> +static int emac_open(struct net_device *ndev)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> + struct device *dev = &priv->pdev->dev;
> + int ret;
> +
> + ret = emac_alloc_tx_resources(priv);
> + if (ret) {
> + dev_err(dev, "Cannot allocate TX resources\n");
> + goto err;
> + }
> +
> + ret = emac_alloc_rx_resources(priv);
> + if (ret) {
> + dev_err(dev, "Cannot allocate RX resources\n");
> + goto err_free_tx;
> + }
> +
> + ret = emac_up(priv);
> + if (ret) {
> + dev_err(dev, "Error when bringing interface up\n");
> + goto err_free_rx;
> + }
> + return 0;
> +
> +err_free_rx:
> + emac_free_rx_resources(priv);
> +err_free_tx:
> + emac_free_tx_resources(priv);
> +err:
> + return ret;
> +}
> +
> +/* Called when interface is brought down. */
> +static int emac_stop(struct net_device *ndev)
> +{
> + struct emac_priv *priv = netdev_priv(ndev);
> +
> + emac_down(priv);
> + emac_free_tx_resources(priv);
> + emac_free_rx_resources(priv);
> +
> + return 0;
> +}
> +
> +static const struct ethtool_ops emac_ethtool_ops = {
> + .get_link_ksettings = phy_ethtool_get_link_ksettings,
> + .set_link_ksettings = phy_ethtool_set_link_ksettings,
> + .nway_reset = phy_ethtool_nway_reset,
> + .get_drvinfo = emac_get_drvinfo,
> + .get_link = ethtool_op_get_link,
> +
> + .get_regs = emac_ethtool_get_regs,
> + .get_regs_len = emac_ethtool_get_regs_len,
> +
> + .get_rmon_stats = emac_get_rmon_stats,
> + .get_pause_stats = emac_get_pause_stats,
> + .get_eth_mac_stats = emac_get_eth_mac_stats,
> +
> + .get_sset_count = emac_get_sset_count,
> + .get_strings = emac_get_strings,
> + .get_ethtool_stats = emac_get_ethtool_stats,
> +
> + .get_pauseparam = emac_get_pauseparam,
> + .set_pauseparam = emac_set_pauseparam,
> +};
> +
> +static const struct net_device_ops emac_netdev_ops = {
> + .ndo_open = emac_open,
> + .ndo_stop = emac_stop,
> + .ndo_start_xmit = emac_start_xmit,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_set_mac_address = emac_set_mac_address,
> + .ndo_eth_ioctl = phy_do_ioctl_running,
> + .ndo_change_mtu = emac_change_mtu,
> + .ndo_tx_timeout = emac_tx_timeout,
> + .ndo_set_rx_mode = emac_set_rx_mode,
> + .ndo_get_stats64 = emac_get_stats64,
> +};
> +
> +/* Currently we always use 15.6 ps/step for the delay line */
> +
> +static u32 delay_ps_to_unit(u32 ps)
> +{
> + return DIV_ROUND_CLOSEST(ps * 10, 156);
> +}
> +
> +static u32 delay_unit_to_ps(u32 unit)
> +{
> + return DIV_ROUND_CLOSEST(unit * 156, 10);
> +}
> +
> +#define EMAC_MAX_DELAY_UNIT \
> + (EMAC_TX_DLINE_CODE_MASK >> EMAC_TX_DLINE_CODE_SHIFT)
> +
> +/* Minus one just to be safe from rounding errors */
> +#define EMAC_MAX_DELAY_PS (delay_unit_to_ps(EMAC_MAX_DELAY_UNIT - 1))
> +
> +static int emac_config_dt(struct platform_device *pdev, struct emac_priv *priv)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct device *dev = &pdev->dev;
> + u8 mac_addr[ETH_ALEN] = { 0 };
> + int ret;
> +
> + priv->iobase = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(priv->iobase))
> + return dev_err_probe(dev, PTR_ERR(priv->iobase),
> + "ioremap failed\n");
> +
> + priv->regmap_apmu =
> + syscon_regmap_lookup_by_phandle_args(np, "spacemit,apmu", 1,
> + &priv->regmap_apmu_offset);
> +
> + if (IS_ERR(priv->regmap_apmu))
> + return dev_err_probe(dev, PTR_ERR(priv->regmap_apmu),
> + "failed to get syscon\n");
> +
> + priv->irq = platform_get_irq(pdev, 0);
> + if (priv->irq < 0)
> + return priv->irq;
> +
> + ret = of_get_mac_address(np, mac_addr);
> + if (ret) {
> + if (ret == -EPROBE_DEFER)
> + return dev_err_probe(dev, ret,
> + "Can't get MAC address\n");
> +
> + dev_info(&pdev->dev, "Using random MAC address\n");
> + eth_hw_addr_random(priv->ndev);
> + } else {
> + eth_hw_addr_set(priv->ndev, mac_addr);
> + }
> +
> + priv->tx_delay = 0;
> + priv->rx_delay = 0;
> +
> + of_property_read_u32(np, "tx-internal-delay-ps", &priv->tx_delay);
> + of_property_read_u32(np, "rx-internal-delay-ps", &priv->rx_delay);
> +
> + if (priv->tx_delay > EMAC_MAX_DELAY_PS) {
> + dev_err(&pdev->dev,
> + "tx-internal-delay-ps too large: max %d, got %d",
> + EMAC_MAX_DELAY_PS, priv->tx_delay);
> + return -EINVAL;
> + }
> +
> + if (priv->rx_delay > EMAC_MAX_DELAY_PS) {
> + dev_err(&pdev->dev,
> + "rx-internal-delay-ps too large: max %d, got %d",
> + EMAC_MAX_DELAY_PS, priv->rx_delay);
> + return -EINVAL;
> + }
> +
> + priv->tx_delay = delay_ps_to_unit(priv->tx_delay);
> + priv->rx_delay = delay_ps_to_unit(priv->rx_delay);
> +
> + return 0;
> +}
> +
> +static int emac_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct reset_control *reset;
> + struct net_device *ndev;
> + struct emac_priv *priv;
> + int ret;
> +
> + ndev = devm_alloc_etherdev(dev, sizeof(struct emac_priv));
> + if (!ndev)
> + return -ENOMEM;
> +
> + ndev->hw_features = NETIF_F_SG;
> + ndev->features |= ndev->hw_features;
> +
> + ndev->max_mtu = EMAC_RX_BUF_4K - (ETH_HLEN + ETH_FCS_LEN);
> +
> + priv = netdev_priv(ndev);
> + priv->ndev = ndev;
> + priv->pdev = pdev;
> + platform_set_drvdata(pdev, priv);
> +
> + ret = emac_config_dt(pdev, priv);
> + if (ret < 0) {
> + dev_err_probe(dev, ret, "Configuration failed\n");
> + goto err;
> + }
> +
> + ndev->watchdog_timeo = 5 * HZ;
> + ndev->base_addr = (unsigned long)priv->iobase;
> + ndev->irq = priv->irq;
> +
> + ndev->ethtool_ops = &emac_ethtool_ops;
> + ndev->netdev_ops = &emac_netdev_ops;
> +
> + devm_pm_runtime_enable(&pdev->dev);
> +
> + priv->bus_clk = devm_clk_get_enabled(&pdev->dev, NULL);
> + if (IS_ERR(priv->bus_clk)) {
> + ret = dev_err_probe(dev, PTR_ERR(priv->bus_clk),
> + "Failed to get clock\n");
> + goto err;
> + }
> +
> + reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev,
> + NULL);
> + if (IS_ERR(reset)) {
> + ret = dev_err_probe(dev, PTR_ERR(reset),
> + "Failed to get reset\n");
> + goto err;
> + }
> +
> + emac_sw_init(priv);
> +
> + if (of_phy_is_fixed_link(dev->of_node)) {
> + ret = of_phy_register_fixed_link(dev->of_node);
> + if (ret) {
> + dev_err_probe(dev, ret,
> + "Failed to register fixed-link");
> + goto err_timer_delete;
> + }
> + }
> +
> + ret = emac_mdio_init(priv);
> + if (ret)
> + goto err_phy_deregister;
> +
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> + ret = devm_register_netdev(dev, ndev);
> + if (ret) {
> + dev_err(dev, "devm_register_netdev failed\n");
> + goto err_phy_deregister;
> + }
> +
> + netif_napi_add(ndev, &priv->napi, emac_rx_poll);
> + netif_carrier_off(ndev);
> +
> + return 0;
> +
> +err_phy_deregister:
> + if (of_phy_is_fixed_link(dev->of_node))
> + of_phy_deregister_fixed_link(dev->of_node);
> +
> +err_timer_delete:
> + timer_delete_sync(&priv->txtimer);
> + timer_delete_sync(&priv->stats_timer);
> +err:
> + return ret;
> +}
> +
> +static void emac_remove(struct platform_device *pdev)
> +{
> + struct emac_priv *priv = platform_get_drvdata(pdev);
> +
> + timer_shutdown_sync(&priv->txtimer);
> + cancel_work_sync(&priv->tx_timeout_task);
> +
> + timer_shutdown_sync(&priv->stats_timer);
> +
> + emac_reset_hw(priv);
> +
> + if (of_phy_is_fixed_link(pdev->dev.of_node))
> + of_phy_deregister_fixed_link(pdev->dev.of_node);
> +}
> +
> +static int emac_resume(struct device *dev)
> +{
> + struct emac_priv *priv = dev_get_drvdata(dev);
> + struct net_device *ndev = priv->ndev;
> + int ret;
> +
> + ret = clk_prepare_enable(priv->bus_clk);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable bus clock: %d\n", ret);
> + return ret;
> + }
> +
> + if (!netif_running(ndev))
> + return 0;
> +
> + ret = emac_open(ndev);
> + if (ret) {
> + clk_disable_unprepare(priv->bus_clk);
> + return ret;
> + }
> +
> + netif_device_attach(ndev);
> +
> + emac_stats_timer(&priv->stats_timer);
> +
> + return 0;
> +}
> +
> +static int emac_suspend(struct device *dev)
> +{
> + struct emac_priv *priv = dev_get_drvdata(dev);
> + struct net_device *ndev = priv->ndev;
> +
> + if (!ndev || !netif_running(ndev)) {
> + clk_disable_unprepare(priv->bus_clk);
> + return 0;
> + }
> +
> + emac_stop(ndev);
> +
> + clk_disable_unprepare(priv->bus_clk);
> + netif_device_detach(ndev);
> + return 0;
> +}
> +
> +static const struct dev_pm_ops emac_pm_ops = {
> + SYSTEM_SLEEP_PM_OPS(emac_suspend, emac_resume)
> +};
> +
> +static const struct of_device_id emac_of_match[] = {
> + { .compatible = "spacemit,k1-emac" },
> + { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, emac_of_match);
> +
> +static struct platform_driver emac_driver = {
> + .probe = emac_probe,
> + .remove = emac_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = of_match_ptr(emac_of_match),
> + .pm = &emac_pm_ops,
> + },
> +};
> +module_platform_driver(emac_driver);
> +
> +MODULE_DESCRIPTION("SpacemiT K1 Ethernet driver");
> +MODULE_AUTHOR("Vivian Wang <wangruikang@...as.ac.cn>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/net/ethernet/spacemit/k1_emac.h b/drivers/net/ethernet/spacemit/k1_emac.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..ef681f06a50a63cca34561353a7b7ccf3f996853
> --- /dev/null
> +++ b/drivers/net/ethernet/spacemit/k1_emac.h
> @@ -0,0 +1,426 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * SpacemiT K1 Ethernet hardware definitions
> + *
> + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
> + * Copyright (C) 2025 Vivian Wang <wangruikang@...as.ac.cn>
> + */
> +
> +#ifndef _K1_EMAC_H_
> +#define _K1_EMAC_H_
> +
> +/* APMU syscon registers */
> +
> +#define APMU_EMAC_CTRL_REG 0x0
> +
> +#define PHY_INTF_RGMII BIT(2)
> +
> +/*
> + * Only valid for RMII mode
> + * 0: Ref clock from External PHY
> + * 1: Ref clock from SoC
> + */
> +#define REF_CLK_SEL BIT(3)
> +
> +/*
> + * Function clock select
> + * 0: 208 MHz
> + * 1: 312 MHz
> + */
> +#define FUNC_CLK_SEL BIT(4)
> +
> +/* Only valid for RMII, invert TX clk */
> +#define RMII_TX_CLK_SEL BIT(6)
> +
> +/* Only valid for RMII, invert RX clk */
> +#define RMII_RX_CLK_SEL BIT(7)
> +
> +/*
> + * Only valid for RGMII
> + * 0: TX clk from RX clk
> + * 1: TX clk from SoC
> + */
> +#define RGMII_TX_CLK_SEL BIT(8)
> +
> +#define PHY_IRQ_EN BIT(12)
> +#define AXI_SINGLE_ID BIT(13)
> +
> +#define RMII_TX_PHASE_SHIFT 16
> +#define RMII_TX_PHASE_MASK GENMASK(18, 16)
> +#define RMII_RX_PHASE_SHIFT 20
> +#define RMII_RX_PHASE_MASK GENMASK(22, 20)
> +
> +#define RGMII_TX_PHASE_SHIFT 24
> +#define RGMII_TX_PHASE_MASK GENMASK(26, 24)
> +#define RGMII_RX_PHASE_SHIFT 28
> +#define RGMII_RX_PHASE_MASK GENMASK(30, 28)
> +
> +#define APMU_EMAC_DLINE_REG 0x4
> +
> +#define EMAC_RX_DLINE_EN BIT(0)
> +#define EMAC_RX_DLINE_STEP_SHIFT 4
> +#define EMAC_RX_DLINE_STEP_MASK GENMASK(5, 4)
> +#define EMAC_RX_DLINE_CODE_SHIFT 8
> +#define EMAC_RX_DLINE_CODE_MASK GENMASK(15, 8)
> +
> +#define EMAC_TX_DLINE_EN BIT(16)
> +#define EMAC_TX_DLINE_STEP_SHIFT 20
> +#define EMAC_TX_DLINE_STEP_MASK GENMASK(21, 20)
> +#define EMAC_TX_DLINE_CODE_SHIFT 24
> +#define EMAC_TX_DLINE_CODE_MASK GENMASK(31, 24)
> +
> +#define EMAC_DLINE_STEP_15P6 0 /* 15.6 ps/step */
> +#define EMAC_DLINE_STEP_24P4 1 /* 24.4 ps/step */
> +#define EMAC_DLINE_STEP_29P7 2 /* 29.7 ps/step */
> +#define EMAC_DLINE_STEP_35P1 3 /* 35.1 ps/step */
> +
> +/* DMA register set */
> +#define DMA_CONFIGURATION 0x0000
> +#define DMA_CONTROL 0x0004
> +#define DMA_STATUS_IRQ 0x0008
> +#define DMA_INTERRUPT_ENABLE 0x000c
> +
> +#define DMA_TRANSMIT_AUTO_POLL_COUNTER 0x0010
> +#define DMA_TRANSMIT_POLL_DEMAND 0x0014
> +#define DMA_RECEIVE_POLL_DEMAND 0x0018
> +
> +#define DMA_TRANSMIT_BASE_ADDRESS 0x001c
> +#define DMA_RECEIVE_BASE_ADDRESS 0x0020
> +#define DMA_MISSED_FRAME_COUNTER 0x0024
> +#define DMA_STOP_FLUSH_COUNTER 0x0028
> +
> +#define DMA_RECEIVE_IRQ_MITIGATION_CTRL 0x002c
> +
> +#define DMA_CURRENT_TRANSMIT_DESCRIPTOR_POINTER 0x0030
> +#define DMA_CURRENT_TRANSMIT_BUFFER_POINTER 0x0034
> +#define DMA_CURRENT_RECEIVE_DESCRIPTOR_POINTER 0x0038
> +#define DMA_CURRENT_RECEIVE_BUFFER_POINTER 0x003c
> +
> +/* MAC Register set */
> +#define MAC_GLOBAL_CONTROL 0x0100
> +#define MAC_TRANSMIT_CONTROL 0x0104
> +#define MAC_RECEIVE_CONTROL 0x0108
> +#define MAC_MAXIMUM_FRAME_SIZE 0x010c
> +#define MAC_TRANSMIT_JABBER_SIZE 0x0110
> +#define MAC_RECEIVE_JABBER_SIZE 0x0114
> +#define MAC_ADDRESS_CONTROL 0x0118
> +#define MAC_MDIO_CLK_DIV 0x011c
> +#define MAC_ADDRESS1_HIGH 0x0120
> +#define MAC_ADDRESS1_MED 0x0124
> +#define MAC_ADDRESS1_LOW 0x0128
> +#define MAC_ADDRESS2_HIGH 0x012c
> +#define MAC_ADDRESS2_MED 0x0130
> +#define MAC_ADDRESS2_LOW 0x0134
> +#define MAC_ADDRESS3_HIGH 0x0138
> +#define MAC_ADDRESS3_MED 0x013c
> +#define MAC_ADDRESS3_LOW 0x0140
> +#define MAC_ADDRESS4_HIGH 0x0144
> +#define MAC_ADDRESS4_MED 0x0148
> +#define MAC_ADDRESS4_LOW 0x014c
> +#define MAC_MULTICAST_HASH_TABLE1 0x0150
> +#define MAC_MULTICAST_HASH_TABLE2 0x0154
> +#define MAC_MULTICAST_HASH_TABLE3 0x0158
> +#define MAC_MULTICAST_HASH_TABLE4 0x015c
> +#define MAC_FC_CONTROL 0x0160
> +#define MAC_FC_PAUSE_FRAME_GENERATE 0x0164
> +#define MAC_FC_SOURCE_ADDRESS_HIGH 0x0168
> +#define MAC_FC_SOURCE_ADDRESS_MED 0x016c
> +#define MAC_FC_SOURCE_ADDRESS_LOW 0x0170
> +#define MAC_FC_DESTINATION_ADDRESS_HIGH 0x0174
> +#define MAC_FC_DESTINATION_ADDRESS_MED 0x0178
> +#define MAC_FC_DESTINATION_ADDRESS_LOW 0x017c
> +#define MAC_FC_PAUSE_TIME_VALUE 0x0180
> +#define MAC_FC_HIGH_PAUSE_TIME 0x0184
> +#define MAC_FC_LOW_PAUSE_TIME 0x0188
> +#define MAC_FC_PAUSE_HIGH_THRESHOLD 0x018c
> +#define MAC_FC_PAUSE_LOW_THRESHOLD 0x0190
> +#define MAC_MDIO_CONTROL 0x01a0
> +#define MAC_MDIO_DATA 0x01a4
> +#define MAC_RX_STATCTR_CONTROL 0x01a8
> +#define MAC_RX_STATCTR_DATA_HIGH 0x01ac
> +#define MAC_RX_STATCTR_DATA_LOW 0x01b0
> +#define MAC_TX_STATCTR_CONTROL 0x01b4
> +#define MAC_TX_STATCTR_DATA_HIGH 0x01b8
> +#define MAC_TX_STATCTR_DATA_LOW 0x01bc
> +#define MAC_TRANSMIT_FIFO_ALMOST_FULL 0x01c0
> +#define MAC_TRANSMIT_PACKET_START_THRESHOLD 0x01c4
> +#define MAC_RECEIVE_PACKET_START_THRESHOLD 0x01c8
> +#define MAC_STATUS_IRQ 0x01e0
> +#define MAC_INTERRUPT_ENABLE 0x01e4
> +
> +/* Used for register dump */
> +#define EMAC_DMA_REG_CNT 16
> +#define EMAC_MAC_REG_CNT 124
> +
> +/* DMA_CONFIGURATION (0x0000) */
> +
> +/*
> + * 0-DMA controller in normal operation mode,
> + * 1-DMA controller reset to default state,
> + * clearing all internal state information
> + */
> +#define MREGBIT_SOFTWARE_RESET BIT(0)
> +
> +#define MREGBIT_BURST_1WORD BIT(1)
> +#define MREGBIT_BURST_2WORD BIT(2)
> +#define MREGBIT_BURST_4WORD BIT(3)
> +#define MREGBIT_BURST_8WORD BIT(4)
> +#define MREGBIT_BURST_16WORD BIT(5)
> +#define MREGBIT_BURST_32WORD BIT(6)
> +#define MREGBIT_BURST_64WORD BIT(7)
> +#define MREGBIT_BURST_LENGTH GENMASK(7, 1)
> +#define MREGBIT_DESCRIPTOR_SKIP_LENGTH GENMASK(12, 8)
> +
> +/* For Receive and Transmit DMA operate in Big-Endian mode for Descriptors. */
> +#define MREGBIT_DESCRIPTOR_BYTE_ORDERING BIT(13)
> +
> +#define MREGBIT_BIG_LITLE_ENDIAN BIT(14)
> +#define MREGBIT_TX_RX_ARBITRATION BIT(15)
> +#define MREGBIT_WAIT_FOR_DONE BIT(16)
> +#define MREGBIT_STRICT_BURST BIT(17)
> +#define MREGBIT_DMA_64BIT_MODE BIT(18)
> +
> +/* DMA_CONTROL (0x0004) */
> +#define MREGBIT_START_STOP_TRANSMIT_DMA BIT(0)
> +#define MREGBIT_START_STOP_RECEIVE_DMA BIT(1)
> +
> +/* DMA_STATUS_IRQ (0x0008) */
> +#define MREGBIT_TRANSMIT_TRANSFER_DONE_IRQ BIT(0)
> +#define MREGBIT_TRANSMIT_DES_UNAVAILABLE_IRQ BIT(1)
> +#define MREGBIT_TRANSMIT_DMA_STOPPED_IRQ BIT(2)
> +#define MREGBIT_RECEIVE_TRANSFER_DONE_IRQ BIT(4)
> +#define MREGBIT_RECEIVE_DES_UNAVAILABLE_IRQ BIT(5)
> +#define MREGBIT_RECEIVE_DMA_STOPPED_IRQ BIT(6)
> +#define MREGBIT_RECEIVE_MISSED_FRAME_IRQ BIT(7)
> +#define MREGBIT_MAC_IRQ BIT(8)
> +#define MREGBIT_TRANSMIT_DMA_STATE GENMASK(18, 16)
> +#define MREGBIT_RECEIVE_DMA_STATE GENMASK(23, 20)
> +
> +/* DMA_INTERRUPT_ENABLE (0x000c) */
> +#define MREGBIT_TRANSMIT_TRANSFER_DONE_INTR_ENABLE BIT(0)
> +#define MREGBIT_TRANSMIT_DES_UNAVAILABLE_INTR_ENABLE BIT(1)
> +#define MREGBIT_TRANSMIT_DMA_STOPPED_INTR_ENABLE BIT(2)
> +#define MREGBIT_RECEIVE_TRANSFER_DONE_INTR_ENABLE BIT(4)
> +#define MREGBIT_RECEIVE_DES_UNAVAILABLE_INTR_ENABLE BIT(5)
> +#define MREGBIT_RECEIVE_DMA_STOPPED_INTR_ENABLE BIT(6)
> +#define MREGBIT_RECEIVE_MISSED_FRAME_INTR_ENABLE BIT(7)
> +#define MREGBIT_MAC_INTR_ENABLE BIT(8)
> +
> +/* DMA_RECEIVE_IRQ_MITIGATION_CTRL (0x002c) */
> +#define MREGBIT_RECEIVE_IRQ_FRAME_COUNTER_MASK GENMASK(7, 0)
> +#define MREGBIT_RECEIVE_IRQ_TIMEOUT_COUNTER_SHIFT 8
> +#define MREGBIT_RECEIVE_IRQ_TIMEOUT_COUNTER_MASK GENMASK(27, 8)
> +#define MREGBIT_RECEIVE_IRQ_FRAME_COUNTER_MODE BIT(30)
> +#define MREGBIT_RECEIVE_IRQ_MITIGATION_ENABLE BIT(31)
> +
> +/* MAC_GLOBAL_CONTROL (0x0100) */
> +#define MREGBIT_SPEED GENMASK(1, 0)
> +#define MREGBIT_SPEED_10M 0x0
> +#define MREGBIT_SPEED_100M BIT(0)
> +#define MREGBIT_SPEED_1000M BIT(1)
> +#define MREGBIT_FULL_DUPLEX_MODE BIT(2)
> +#define MREGBIT_RESET_RX_STAT_COUNTERS BIT(3)
> +#define MREGBIT_RESET_TX_STAT_COUNTERS BIT(4)
> +#define MREGBIT_UNICAST_WAKEUP_MODE BIT(8)
> +#define MREGBIT_MAGIC_PACKET_WAKEUP_MODE BIT(9)
> +
> +/* MAC_TRANSMIT_CONTROL (0x0104) */
> +#define MREGBIT_TRANSMIT_ENABLE BIT(0)
> +#define MREGBIT_INVERT_FCS BIT(1)
> +#define MREGBIT_DISABLE_FCS_INSERT BIT(2)
> +#define MREGBIT_TRANSMIT_AUTO_RETRY BIT(3)
> +#define MREGBIT_IFG_LEN GENMASK(6, 4)
> +#define MREGBIT_PREAMBLE_LENGTH GENMASK(9, 7)
> +
> +/* MAC_RECEIVE_CONTROL (0x0108) */
> +#define MREGBIT_RECEIVE_ENABLE BIT(0)
> +#define MREGBIT_DISABLE_FCS_CHECK BIT(1)
> +#define MREGBIT_STRIP_FCS BIT(2)
> +#define MREGBIT_STORE_FORWARD BIT(3)
> +#define MREGBIT_STATUS_FIRST BIT(4)
> +#define MREGBIT_PASS_BAD_FRAMES BIT(5)
> +#define MREGBIT_ACOOUNT_VLAN BIT(6)
> +
> +/* MAC_MAXIMUM_FRAME_SIZE (0x010c) */
> +#define MREGBIT_MAX_FRAME_SIZE GENMASK(13, 0)
> +
> +/* MAC_TRANSMIT_JABBER_SIZE (0x0110) */
> +#define MREGBIT_TRANSMIT_JABBER_SIZE GENMASK(15, 0)
> +
> +/* MAC_RECEIVE_JABBER_SIZE (0x0114) */
> +#define MREGBIT_RECEIVE_JABBER_SIZE GENMASK(15, 0)
> +
> +/* MAC_ADDRESS_CONTROL (0x0118) */
> +#define MREGBIT_MAC_ADDRESS1_ENABLE BIT(0)
> +#define MREGBIT_MAC_ADDRESS2_ENABLE BIT(1)
> +#define MREGBIT_MAC_ADDRESS3_ENABLE BIT(2)
> +#define MREGBIT_MAC_ADDRESS4_ENABLE BIT(3)
> +#define MREGBIT_INVERSE_MAC_ADDRESS1_ENABLE BIT(4)
> +#define MREGBIT_INVERSE_MAC_ADDRESS2_ENABLE BIT(5)
> +#define MREGBIT_INVERSE_MAC_ADDRESS3_ENABLE BIT(6)
> +#define MREGBIT_INVERSE_MAC_ADDRESS4_ENABLE BIT(7)
> +#define MREGBIT_PROMISCUOUS_MODE BIT(8)
> +
> +/* MAC_FC_CONTROL (0x0160) */
> +#define MREGBIT_FC_DECODE_ENABLE BIT(0)
> +#define MREGBIT_FC_GENERATION_ENABLE BIT(1)
> +#define MREGBIT_AUTO_FC_GENERATION_ENABLE BIT(2)
> +#define MREGBIT_MULTICAST_MODE BIT(3)
> +#define MREGBIT_BLOCK_PAUSE_FRAMES BIT(4)
> +
> +/* MAC_FC_PAUSE_FRAME_GENERATE (0x0164) */
> +#define MREGBIT_GENERATE_PAUSE_FRAME BIT(0)
> +
> +/* MAC_FC_PAUSE_TIME_VALUE (0x0180) */
> +#define MREGBIT_MAC_FC_PAUSE_TIME GENMASK(15, 0)
> +
> +/* MAC_MDIO_CONTROL (0x01a0) */
> +#define MREGBIT_PHY_ADDRESS GENMASK(4, 0)
> +#define MREGBIT_REGISTER_ADDRESS GENMASK(9, 5)
> +#define MREGBIT_MDIO_READ_WRITE BIT(10)
> +#define MREGBIT_START_MDIO_TRANS BIT(15)
> +
> +/* MAC_MDIO_DATA (0x01a4) */
> +#define MREGBIT_MDIO_DATA GENMASK(15, 0)
> +
> +/* MAC_RX_STATCTR_CONTROL (0x01a8) */
> +#define MREGBIT_RX_COUNTER_NUMBER GENMASK(4, 0)
> +#define MREGBIT_START_RX_COUNTER_READ BIT(15)
> +
> +/* MAC_RX_STATCTR_DATA_HIGH (0x01ac) */
> +#define MREGBIT_RX_STATCTR_DATA_HIGH GENMASK(15, 0)
> +/* MAC_RX_STATCTR_DATA_LOW (0x01b0) */
> +#define MREGBIT_RX_STATCTR_DATA_LOW GENMASK(15, 0)
> +
> +/* MAC_TX_STATCTR_CONTROL (0x01b4) */
> +#define MREGBIT_TX_COUNTER_NUMBER GENMASK(4, 0)
> +#define MREGBIT_START_TX_COUNTER_READ BIT(15)
> +
> +/* MAC_TX_STATCTR_DATA_HIGH (0x01b8) */
> +#define MREGBIT_TX_STATCTR_DATA_HIGH GENMASK(15, 0)
> +/* MAC_TX_STATCTR_DATA_LOW (0x01bc) */
> +#define MREGBIT_TX_STATCTR_DATA_LOW GENMASK(15, 0)
> +
> +/* MAC_TRANSMIT_FIFO_ALMOST_FULL (0x01c0) */
> +#define MREGBIT_TX_FIFO_AF GENMASK(13, 0)
> +
> +/* MAC_TRANSMIT_PACKET_START_THRESHOLD (0x01c4) */
> +#define MREGBIT_TX_PACKET_START_THRESHOLD GENMASK(13, 0)
> +
> +/* MAC_RECEIVE_PACKET_START_THRESHOLD (0x01c8) */
> +#define MREGBIT_RX_PACKET_START_THRESHOLD GENMASK(13, 0)
> +
> +/* MAC_STATUS_IRQ (0x01e0) */
> +#define MREGBIT_MAC_UNDERRUN_IRQ BIT(0)
> +#define MREGBIT_MAC_JABBER_IRQ BIT(1)
> +
> +/* MAC_INTERRUPT_ENABLE (0x01e4) */
> +#define MREGBIT_MAC_UNDERRUN_INTERRUPT_ENABLE BIT(0)
> +#define MREGBIT_JABBER_INTERRUPT_ENABLE BIT(1)
> +
> +/* RX DMA descriptor */
> +
> +#define RX_DESC_0_FRAME_PACKET_LENGTH_SHIFT 0
> +#define RX_DESC_0_FRAME_PACKET_LENGTH_MASK GENMASK(13, 0)
> +#define RX_DESC_0_FRAME_ALIGN_ERR BIT(14)
> +#define RX_DESC_0_FRAME_RUNT BIT(15)
> +#define RX_DESC_0_FRAME_ETHERNET_TYPE BIT(16)
> +#define RX_DESC_0_FRAME_VLAN BIT(17)
> +#define RX_DESC_0_FRAME_MULTICAST BIT(18)
> +#define RX_DESC_0_FRAME_BROADCAST BIT(19)
> +#define RX_DESC_0_FRAME_CRC_ERR BIT(20)
> +#define RX_DESC_0_FRAME_MAX_LEN_ERR BIT(21)
> +#define RX_DESC_0_FRAME_JABBER_ERR BIT(22)
> +#define RX_DESC_0_FRAME_LENGTH_ERR BIT(23)
> +#define RX_DESC_0_FRAME_MAC_ADDR1_MATCH BIT(24)
> +#define RX_DESC_0_FRAME_MAC_ADDR2_MATCH BIT(25)
> +#define RX_DESC_0_FRAME_MAC_ADDR3_MATCH BIT(26)
> +#define RX_DESC_0_FRAME_MAC_ADDR4_MATCH BIT(27)
> +#define RX_DESC_0_FRAME_PAUSE_CTRL BIT(28)
> +#define RX_DESC_0_LAST_DESCRIPTOR BIT(29)
> +#define RX_DESC_0_FIRST_DESCRIPTOR BIT(30)
> +#define RX_DESC_0_OWN BIT(31)
> +
> +#define RX_DESC_1_BUFFER_SIZE_1_SHIFT 0
> +#define RX_DESC_1_BUFFER_SIZE_1_MASK GENMASK(11, 0)
> +#define RX_DESC_1_BUFFER_SIZE_2_SHIFT 12
> +#define RX_DESC_1_BUFFER_SIZE_2_MASK GENMASK(23, 12)
> + /* [24] reserved */
> +#define RX_DESC_1_SECOND_ADDRESS_CHAINED BIT(25)
> +#define RX_DESC_1_END_RING BIT(26)
> + /* [29:27] reserved */
> +#define RX_DESC_1_RX_TIMESTAMP BIT(30)
> +#define RX_DESC_1_PTP_PKT BIT(31)
> +
> +/* TX DMA descriptor */
> +
> + /* [29:0] unused */
> +#define TX_DESC_0_TX_TIMESTAMP BIT(30)
> +#define TX_DESC_0_OWN BIT(31)
> +
> +#define TX_DESC_1_BUFFER_SIZE_1_SHIFT 0
> +#define TX_DESC_1_BUFFER_SIZE_1_MASK GENMASK(11, 0)
> +#define TX_DESC_1_BUFFER_SIZE_2_SHIFT 12
> +#define TX_DESC_1_BUFFER_SIZE_2_MASK GENMASK(23, 12)
> +#define TX_DESC_1_FORCE_EOP_ERROR BIT(24)
> +#define TX_DESC_1_SECOND_ADDRESS_CHAINED BIT(25)
> +#define TX_DESC_1_END_RING BIT(26)
> +#define TX_DESC_1_DISABLE_PADDING BIT(27)
> +#define TX_DESC_1_ADD_CRC_DISABLE BIT(28)
> +#define TX_DESC_1_FIRST_SEGMENT BIT(29)
> +#define TX_DESC_1_LAST_SEGMENT BIT(30)
> +#define TX_DESC_1_INTERRUPT_ON_COMPLETION BIT(31)
> +
> +struct emac_desc {
> + u32 desc0;
> + u32 desc1;
> + u32 buffer_addr_1;
> + u32 buffer_addr_2;
> +};
> +
> +/* Keep stats in this order, index used for accessing hardware */
> +
> +struct emac_hw_tx_stats {
> + u64 tx_ok_pkts;
> + u64 tx_total_pkts;
> + u64 tx_ok_bytes;
> + u64 tx_err_pkts;
> + u64 tx_singleclsn_pkts;
> + u64 tx_multiclsn_pkts;
> + u64 tx_lateclsn_pkts;
> + u64 tx_excessclsn_pkts;
> + u64 tx_unicast_pkts;
> + u64 tx_multicast_pkts;
> + u64 tx_broadcast_pkts;
> + u64 tx_pause_pkts;
> +};
> +
> +struct emac_hw_rx_stats {
> + u64 rx_ok_pkts;
> + u64 rx_total_pkts;
> + u64 rx_crc_err_pkts;
> + u64 rx_align_err_pkts;
> + u64 rx_err_total_pkts;
> + u64 rx_ok_bytes;
> + u64 rx_total_bytes;
> + u64 rx_unicast_pkts;
> + u64 rx_multicast_pkts;
> + u64 rx_broadcast_pkts;
> + u64 rx_pause_pkts;
> + u64 rx_len_err_pkts;
> + u64 rx_len_undersize_pkts;
> + u64 rx_len_oversize_pkts;
> + u64 rx_len_fragment_pkts;
> + u64 rx_len_jabber_pkts;
> + u64 rx_64_pkts;
> + u64 rx_65_127_pkts;
> + u64 rx_128_255_pkts;
> + u64 rx_256_511_pkts;
> + u64 rx_512_1023_pkts;
> + u64 rx_1024_1518_pkts;
> + u64 rx_1519_plus_pkts;
> + u64 rx_drp_fifo_full_pkts;
> + u64 rx_truncate_fifo_full_pkts;
> +};
> +
> +#endif /* _K1_EMAC_H_ */
>
> --
> 2.50.1
>
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@...ts.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
Powered by blists - more mailing lists