13d0407baSopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * Copyright (c) 2020 Fuzhou Rockchip Electronics Co., Ltd
43d0407baSopenharmony_ci */
53d0407baSopenharmony_ci
63d0407baSopenharmony_ci#include <linux/ip.h>
73d0407baSopenharmony_ci#include <linux/tcp.h>
83d0407baSopenharmony_ci#include <linux/skbuff.h>
93d0407baSopenharmony_ci#include <linux/if_ether.h>
103d0407baSopenharmony_ci#include <linux/if.h>
113d0407baSopenharmony_ci#include <linux/dma-mapping.h>
123d0407baSopenharmony_ci#include <linux/of_device.h>
133d0407baSopenharmony_ci#include <linux/slab.h>
143d0407baSopenharmony_ci#include <linux/prefetch.h>
153d0407baSopenharmony_ci#include <linux/regmap.h>
163d0407baSopenharmony_ci#include <linux/phy.h>
173d0407baSopenharmony_ci#include <linux/udp.h>
183d0407baSopenharmony_ci#include <linux/skbuff.h>
193d0407baSopenharmony_ci#include <net/pkt_cls.h>
203d0407baSopenharmony_ci#include <net/tcp.h>
213d0407baSopenharmony_ci#include <net/udp.h>
223d0407baSopenharmony_ci#include <linux/soc/rockchip/rk_vendor_storage.h>
233d0407baSopenharmony_ci#include "stmmac.h"
243d0407baSopenharmony_ci#include "dwmac1000.h"
253d0407baSopenharmony_ci#include "dwmac_dma.h"
263d0407baSopenharmony_ci#include "dwmac-rk-tool.h"
273d0407baSopenharmony_ci
283d0407baSopenharmony_cienum { LOOPBACK_TYPE_GMAC = 1, LOOPBACK_TYPE_PHY };
293d0407baSopenharmony_ci
303d0407baSopenharmony_cienum { LOOPBACK_SPEED10 = 10, LOOPBACK_SPEED100 = 100, LOOPBACK_SPEED1000 = 1000 };
313d0407baSopenharmony_ci
323d0407baSopenharmony_cistruct dwmac_rk_packet_attrs {
333d0407baSopenharmony_ci    unsigned char src[6];
343d0407baSopenharmony_ci    unsigned char dst[6];
353d0407baSopenharmony_ci    u32 ip_src;
363d0407baSopenharmony_ci    u32 ip_dst;
373d0407baSopenharmony_ci    int tcp;
383d0407baSopenharmony_ci    int sport;
393d0407baSopenharmony_ci    int dport;
403d0407baSopenharmony_ci    int size;
413d0407baSopenharmony_ci};
423d0407baSopenharmony_ci
433d0407baSopenharmony_cistruct dwmac_rk_hdr {
443d0407baSopenharmony_ci    __be32 version;
453d0407baSopenharmony_ci    __be64 magic;
463d0407baSopenharmony_ci    u32 id;
473d0407baSopenharmony_ci    int tx;
483d0407baSopenharmony_ci    int rx;
493d0407baSopenharmony_ci} packed;
503d0407baSopenharmony_ci
513d0407baSopenharmony_cistruct dwmac_rk_lb_priv {
523d0407baSopenharmony_ci    /* desc && buffer */
533d0407baSopenharmony_ci    struct dma_desc *dma_tx;
543d0407baSopenharmony_ci    dma_addr_t dma_tx_phy;
553d0407baSopenharmony_ci    struct sk_buff *tx_skbuff;
563d0407baSopenharmony_ci    dma_addr_t tx_skbuff_dma;
573d0407baSopenharmony_ci    unsigned int tx_skbuff_dma_len;
583d0407baSopenharmony_ci
593d0407baSopenharmony_ci    struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
603d0407baSopenharmony_ci    dma_addr_t dma_rx_phy;
613d0407baSopenharmony_ci    struct sk_buff *rx_skbuff;
623d0407baSopenharmony_ci    dma_addr_t rx_skbuff_dma;
633d0407baSopenharmony_ci    u32 rx_tail_addr;
643d0407baSopenharmony_ci    u32 tx_tail_addr;
653d0407baSopenharmony_ci
663d0407baSopenharmony_ci    /* rx buffer size */
673d0407baSopenharmony_ci    unsigned int dma_buf_sz;
683d0407baSopenharmony_ci    unsigned int buf_sz;
693d0407baSopenharmony_ci
703d0407baSopenharmony_ci    int type;
713d0407baSopenharmony_ci    int speed;
723d0407baSopenharmony_ci    struct dwmac_rk_packet_attrs *packet;
733d0407baSopenharmony_ci
743d0407baSopenharmony_ci    unsigned int actual_size;
753d0407baSopenharmony_ci    int scan;
763d0407baSopenharmony_ci    int sysfs;
773d0407baSopenharmony_ci    u32 id;
783d0407baSopenharmony_ci    int tx;
793d0407baSopenharmony_ci    int rx;
803d0407baSopenharmony_ci    int final_tx;
813d0407baSopenharmony_ci    int final_rx;
823d0407baSopenharmony_ci    int max_delay;
833d0407baSopenharmony_ci};
843d0407baSopenharmony_ci
853d0407baSopenharmony_ci#define DMA_CONTROL_OSP BIT(4)
863d0407baSopenharmony_ci#define DMA_CHAN_BASE_ADDR 0x00001100
873d0407baSopenharmony_ci#define DMA_CHAN_BASE_OFFSET 0x80
883d0407baSopenharmony_ci#define DMA_CHANX_BASE_ADDR(x) (DMA_CHAN_BASE_ADDR + ((x)*DMA_CHAN_BASE_OFFSET))
893d0407baSopenharmony_ci#define DMA_CHAN_TX_CONTROL(x) (DMA_CHANX_BASE_ADDR(x) + 0x4)
903d0407baSopenharmony_ci#define DMA_CHAN_STATUS(x) (DMA_CHANX_BASE_ADDR(x) + 0x60)
913d0407baSopenharmony_ci#define DMA_CHAN_STATUS_ERI BIT(11)
923d0407baSopenharmony_ci#define DMA_CHAN_STATUS_ETI BIT(10)
933d0407baSopenharmony_ci
943d0407baSopenharmony_ci#define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES)
953d0407baSopenharmony_ci#define MAX_DELAYLINE 0x7f
963d0407baSopenharmony_ci#define RK3588_MAX_DELAYLINE 0xc7
973d0407baSopenharmony_ci#define SCAN_STEP 0x5
983d0407baSopenharmony_ci#define SCAN_VALID_RANGE 0xA
993d0407baSopenharmony_ci
1003d0407baSopenharmony_ci#define DWMAC_RK_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct dwmac_rk_hdr))
1013d0407baSopenharmony_ci#define DWMAC_RK_TEST_PKT_MAGIC 0xdeadcafecafedeadULL
1023d0407baSopenharmony_ci#define DWMAC_RK_TEST_PKT_MAX_SIZE 1500
1033d0407baSopenharmony_ci
1043d0407baSopenharmony_cistatic __maybe_unused struct dwmac_rk_packet_attrs dwmac_rk_udp_attr = {
1053d0407baSopenharmony_ci    .dst = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1063d0407baSopenharmony_ci    .tcp = 0,
1073d0407baSopenharmony_ci    .size = 1024,
1083d0407baSopenharmony_ci};
1093d0407baSopenharmony_ci
1103d0407baSopenharmony_cistatic __maybe_unused struct dwmac_rk_packet_attrs dwmac_rk_tcp_attr = {
1113d0407baSopenharmony_ci    .dst = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1123d0407baSopenharmony_ci    .tcp = 1,
1133d0407baSopenharmony_ci    .size = 1024,
1143d0407baSopenharmony_ci};
1153d0407baSopenharmony_ci
1163d0407baSopenharmony_cistatic int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed, int addr, bool phy)
1173d0407baSopenharmony_ci{
1183d0407baSopenharmony_ci    u32 ctrl;
1193d0407baSopenharmony_ci    int phy_val;
1203d0407baSopenharmony_ci
1213d0407baSopenharmony_ci    ctrl = readl(priv->ioaddr + GMAC_CONTROL);
1223d0407baSopenharmony_ci    ctrl &= ~priv->hw->link.speed_mask;
1233d0407baSopenharmony_ci    ctrl |= GMAC_CONTROL_LM;
1243d0407baSopenharmony_ci
1253d0407baSopenharmony_ci    if (phy) {
1263d0407baSopenharmony_ci        phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
1273d0407baSopenharmony_ci    }
1283d0407baSopenharmony_ci
1293d0407baSopenharmony_ci    switch (speed) {
1303d0407baSopenharmony_ci        case LOOPBACK_SPEED1000:
1313d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed1000;
1323d0407baSopenharmony_ci            if (phy) {
1333d0407baSopenharmony_ci                phy_val &= ~BMCR_SPEED100;
1343d0407baSopenharmony_ci                phy_val |= BMCR_SPEED1000;
1353d0407baSopenharmony_ci            }
1363d0407baSopenharmony_ci            break;
1373d0407baSopenharmony_ci        case LOOPBACK_SPEED100:
1383d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed100;
1393d0407baSopenharmony_ci            if (phy) {
1403d0407baSopenharmony_ci                phy_val &= ~BMCR_SPEED1000;
1413d0407baSopenharmony_ci                phy_val |= BMCR_SPEED100;
1423d0407baSopenharmony_ci            }
1433d0407baSopenharmony_ci            break;
1443d0407baSopenharmony_ci        case LOOPBACK_SPEED10:
1453d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed10;
1463d0407baSopenharmony_ci            if (phy) {
1473d0407baSopenharmony_ci                phy_val &= ~BMCR_SPEED1000;
1483d0407baSopenharmony_ci                phy_val &= ~BMCR_SPEED100;
1493d0407baSopenharmony_ci            }
1503d0407baSopenharmony_ci            break;
1513d0407baSopenharmony_ci        default:
1523d0407baSopenharmony_ci            return -EPERM;
1533d0407baSopenharmony_ci    }
1543d0407baSopenharmony_ci
1553d0407baSopenharmony_ci    ctrl |= priv->hw->link.duplex;
1563d0407baSopenharmony_ci    writel(ctrl, priv->ioaddr + GMAC_CONTROL);
1573d0407baSopenharmony_ci
1583d0407baSopenharmony_ci    if (phy) {
1593d0407baSopenharmony_ci        phy_val &= ~BMCR_PDOWN;
1603d0407baSopenharmony_ci        phy_val &= ~BMCR_ANENABLE;
1613d0407baSopenharmony_ci        phy_val &= ~BMCR_PDOWN;
1623d0407baSopenharmony_ci        phy_val |= BMCR_FULLDPLX;
1633d0407baSopenharmony_ci        mdiobus_write(priv->mii, addr, MII_BMCR, phy_val);
1643d0407baSopenharmony_ci        phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
1653d0407baSopenharmony_ci    }
1663d0407baSopenharmony_ci
1673d0407baSopenharmony_ci    if (likely(priv->plat->fix_mac_speed)) {
1683d0407baSopenharmony_ci        priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed);
1693d0407baSopenharmony_ci    }
1703d0407baSopenharmony_ci
1713d0407baSopenharmony_ci    return 0;
1723d0407baSopenharmony_ci}
1733d0407baSopenharmony_ci
1743d0407baSopenharmony_cistatic int dwmac_rk_disable_mac_loopback(struct stmmac_priv *priv, int addr)
1753d0407baSopenharmony_ci{
1763d0407baSopenharmony_ci    u32 ctrl;
1773d0407baSopenharmony_ci    int phy_val;
1783d0407baSopenharmony_ci
1793d0407baSopenharmony_ci    ctrl = readl(priv->ioaddr + GMAC_CONTROL);
1803d0407baSopenharmony_ci    ctrl &= ~GMAC_CONTROL_LM;
1813d0407baSopenharmony_ci    writel(ctrl, priv->ioaddr + GMAC_CONTROL);
1823d0407baSopenharmony_ci
1833d0407baSopenharmony_ci    phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
1843d0407baSopenharmony_ci    phy_val |= BMCR_ANENABLE;
1853d0407baSopenharmony_ci
1863d0407baSopenharmony_ci    mdiobus_write(priv->mii, addr, MII_BMCR, phy_val);
1873d0407baSopenharmony_ci    phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
1883d0407baSopenharmony_ci
1893d0407baSopenharmony_ci    return 0;
1903d0407baSopenharmony_ci}
1913d0407baSopenharmony_ci
1923d0407baSopenharmony_cistatic int dwmac_rk_set_mac_loopback(struct stmmac_priv *priv, int speed, bool enable, int addr, bool phy)
1933d0407baSopenharmony_ci{
1943d0407baSopenharmony_ci    if (enable) {
1953d0407baSopenharmony_ci        return dwmac_rk_enable_mac_loopback(priv, speed, addr, phy);
1963d0407baSopenharmony_ci    } else {
1973d0407baSopenharmony_ci        return dwmac_rk_disable_mac_loopback(priv, addr);
1983d0407baSopenharmony_ci    }
1993d0407baSopenharmony_ci}
2003d0407baSopenharmony_ci
2013d0407baSopenharmony_cistatic int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed, int addr, bool phy)
2023d0407baSopenharmony_ci{
2033d0407baSopenharmony_ci    u32 ctrl;
2043d0407baSopenharmony_ci    int val;
2053d0407baSopenharmony_ci
2063d0407baSopenharmony_ci    ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
2073d0407baSopenharmony_ci    ctrl &= ~priv->hw->link.speed_mask;
2083d0407baSopenharmony_ci
2093d0407baSopenharmony_ci    if (phy) {
2103d0407baSopenharmony_ci        val = mdiobus_read(priv->mii, addr, MII_BMCR);
2113d0407baSopenharmony_ci    }
2123d0407baSopenharmony_ci
2133d0407baSopenharmony_ci    switch (speed) {
2143d0407baSopenharmony_ci        case LOOPBACK_SPEED1000:
2153d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed1000;
2163d0407baSopenharmony_ci            if (phy) {
2173d0407baSopenharmony_ci                val &= ~BMCR_SPEED100;
2183d0407baSopenharmony_ci                val |= BMCR_SPEED1000;
2193d0407baSopenharmony_ci            }
2203d0407baSopenharmony_ci            break;
2213d0407baSopenharmony_ci        case LOOPBACK_SPEED100:
2223d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed100;
2233d0407baSopenharmony_ci            if (phy) {
2243d0407baSopenharmony_ci                val &= ~BMCR_SPEED1000;
2253d0407baSopenharmony_ci                val |= BMCR_SPEED100;
2263d0407baSopenharmony_ci            }
2273d0407baSopenharmony_ci            break;
2283d0407baSopenharmony_ci        case LOOPBACK_SPEED10:
2293d0407baSopenharmony_ci            ctrl |= priv->hw->link.speed10;
2303d0407baSopenharmony_ci            if (phy) {
2313d0407baSopenharmony_ci                val &= ~BMCR_SPEED1000;
2323d0407baSopenharmony_ci                val &= ~BMCR_SPEED100;
2333d0407baSopenharmony_ci            }
2343d0407baSopenharmony_ci            break;
2353d0407baSopenharmony_ci        default:
2363d0407baSopenharmony_ci            return -EPERM;
2373d0407baSopenharmony_ci    }
2383d0407baSopenharmony_ci
2393d0407baSopenharmony_ci    ctrl |= priv->hw->link.duplex;
2403d0407baSopenharmony_ci    writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
2413d0407baSopenharmony_ci
2423d0407baSopenharmony_ci    if (phy) {
2433d0407baSopenharmony_ci        val |= BMCR_FULLDPLX;
2443d0407baSopenharmony_ci        val &= ~BMCR_PDOWN;
2453d0407baSopenharmony_ci        val &= ~BMCR_ANENABLE;
2463d0407baSopenharmony_ci        val |= BMCR_LOOPBACK;
2473d0407baSopenharmony_ci        mdiobus_write(priv->mii, addr, MII_BMCR, val);
2483d0407baSopenharmony_ci        val = mdiobus_read(priv->mii, addr, MII_BMCR);
2493d0407baSopenharmony_ci    }
2503d0407baSopenharmony_ci
2513d0407baSopenharmony_ci    if (likely(priv->plat->fix_mac_speed)) {
2523d0407baSopenharmony_ci        priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed);
2533d0407baSopenharmony_ci    }
2543d0407baSopenharmony_ci
2553d0407baSopenharmony_ci    return 0;
2563d0407baSopenharmony_ci}
2573d0407baSopenharmony_ci
2583d0407baSopenharmony_cistatic int dwmac_rk_disable_phy_loopback(struct stmmac_priv *priv, int addr)
2593d0407baSopenharmony_ci{
2603d0407baSopenharmony_ci    int val;
2613d0407baSopenharmony_ci
2623d0407baSopenharmony_ci    val = mdiobus_read(priv->mii, addr, MII_BMCR);
2633d0407baSopenharmony_ci    val |= BMCR_ANENABLE;
2643d0407baSopenharmony_ci    val &= ~BMCR_LOOPBACK;
2653d0407baSopenharmony_ci
2663d0407baSopenharmony_ci    mdiobus_write(priv->mii, addr, MII_BMCR, val);
2673d0407baSopenharmony_ci    val = mdiobus_read(priv->mii, addr, MII_BMCR);
2683d0407baSopenharmony_ci
2693d0407baSopenharmony_ci    return 0;
2703d0407baSopenharmony_ci}
2713d0407baSopenharmony_ci
2723d0407baSopenharmony_cistatic int dwmac_rk_set_phy_loopback(struct stmmac_priv *priv, int speed, bool enable, int addr, bool phy)
2733d0407baSopenharmony_ci{
2743d0407baSopenharmony_ci    if (enable) {
2753d0407baSopenharmony_ci        return dwmac_rk_enable_phy_loopback(priv, speed, addr, phy);
2763d0407baSopenharmony_ci    } else {
2773d0407baSopenharmony_ci        return dwmac_rk_disable_phy_loopback(priv, addr);
2783d0407baSopenharmony_ci    }
2793d0407baSopenharmony_ci}
2803d0407baSopenharmony_ci
2813d0407baSopenharmony_cistatic int dwmac_rk_set_loopback(struct stmmac_priv *priv, int type, int speed, bool enable, int addr, bool phy)
2823d0407baSopenharmony_ci{
2833d0407baSopenharmony_ci    int ret;
2843d0407baSopenharmony_ci
2853d0407baSopenharmony_ci    switch (type) {
2863d0407baSopenharmony_ci        case LOOPBACK_TYPE_PHY:
2873d0407baSopenharmony_ci            ret = dwmac_rk_set_phy_loopback(priv, speed, enable, addr, phy);
2883d0407baSopenharmony_ci            break;
2893d0407baSopenharmony_ci        case LOOPBACK_TYPE_GMAC:
2903d0407baSopenharmony_ci            ret = dwmac_rk_set_mac_loopback(priv, speed, enable, addr, phy);
2913d0407baSopenharmony_ci            break;
2923d0407baSopenharmony_ci        default:
2933d0407baSopenharmony_ci            ret = -EOPNOTSUPP;
2943d0407baSopenharmony_ci    }
2953d0407baSopenharmony_ci
2963d0407baSopenharmony_ci    usleep_range(0x186A0, 0x30D40);
2973d0407baSopenharmony_ci    return ret;
2983d0407baSopenharmony_ci}
2993d0407baSopenharmony_ci
3003d0407baSopenharmony_cistatic inline void dwmac_rk_ether_addr_copy(u8 *dst, const u8 *src)
3013d0407baSopenharmony_ci{
3023d0407baSopenharmony_ci    u16 *a = (u16 *)dst;
3033d0407baSopenharmony_ci    const u16 *b = (const u16 *)src;
3043d0407baSopenharmony_ci
3053d0407baSopenharmony_ci    a[0] = b[0];
3063d0407baSopenharmony_ci    a[1] = b[1];
3073d0407baSopenharmony_ci    a[0x02] = b[0x02];
3083d0407baSopenharmony_ci}
3093d0407baSopenharmony_ci
3103d0407baSopenharmony_cistatic void dwmac_rk_udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
3113d0407baSopenharmony_ci{
3123d0407baSopenharmony_ci    struct udphdr *uh = udp_hdr(skb);
3133d0407baSopenharmony_ci    int offset = skb_transport_offset(skb);
3143d0407baSopenharmony_ci    int len = skb->len - offset;
3153d0407baSopenharmony_ci
3163d0407baSopenharmony_ci    skb->csum_start = skb_transport_header(skb) - skb->head;
3173d0407baSopenharmony_ci    skb->csum_offset = offsetof(struct udphdr, check);
3183d0407baSopenharmony_ci    uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0);
3193d0407baSopenharmony_ci}
3203d0407baSopenharmony_ci
3213d0407baSopenharmony_cistatic struct sk_buff *dwmac_rk_get_skb(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
3223d0407baSopenharmony_ci{
3233d0407baSopenharmony_ci    struct sk_buff *skb = NULL;
3243d0407baSopenharmony_ci    struct udphdr *uhdr = NULL;
3253d0407baSopenharmony_ci    struct tcphdr *thdr = NULL;
3263d0407baSopenharmony_ci    struct dwmac_rk_hdr *shdr;
3273d0407baSopenharmony_ci    struct ethhdr *ehdr;
3283d0407baSopenharmony_ci    struct iphdr *ihdr;
3293d0407baSopenharmony_ci    struct dwmac_rk_packet_attrs *attr;
3303d0407baSopenharmony_ci    int iplen, size, nfrags;
3313d0407baSopenharmony_ci
3323d0407baSopenharmony_ci    attr = lb_priv->packet;
3333d0407baSopenharmony_ci    size = attr->size + DWMAC_RK_TEST_PKT_SIZE;
3343d0407baSopenharmony_ci    if (attr->tcp) {
3353d0407baSopenharmony_ci        size += sizeof(struct tcphdr);
3363d0407baSopenharmony_ci    } else {
3373d0407baSopenharmony_ci        size += sizeof(struct udphdr);
3383d0407baSopenharmony_ci    }
3393d0407baSopenharmony_ci
3403d0407baSopenharmony_ci    if (size >= DWMAC_RK_TEST_PKT_MAX_SIZE) {
3413d0407baSopenharmony_ci        return NULL;
3423d0407baSopenharmony_ci    }
3433d0407baSopenharmony_ci
3443d0407baSopenharmony_ci    lb_priv->actual_size = size;
3453d0407baSopenharmony_ci
3463d0407baSopenharmony_ci    skb = netdev_alloc_skb_ip_align(priv->dev, size);
3473d0407baSopenharmony_ci    if (!skb) {
3483d0407baSopenharmony_ci        return NULL;
3493d0407baSopenharmony_ci    }
3503d0407baSopenharmony_ci
3513d0407baSopenharmony_ci    skb_linearize(skb);
3523d0407baSopenharmony_ci    nfrags = skb_shinfo(skb)->nr_frags;
3533d0407baSopenharmony_ci    if (nfrags > 0) {
3543d0407baSopenharmony_ci        pr_err("%s: TX nfrags is not zero\n", __func__);
3553d0407baSopenharmony_ci        dev_kfree_skb(skb);
3563d0407baSopenharmony_ci        return NULL;
3573d0407baSopenharmony_ci    }
3583d0407baSopenharmony_ci
3593d0407baSopenharmony_ci    ehdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
3603d0407baSopenharmony_ci    skb_reset_mac_header(skb);
3613d0407baSopenharmony_ci
3623d0407baSopenharmony_ci    skb_set_network_header(skb, skb->len);
3633d0407baSopenharmony_ci    ihdr = (struct iphdr *)skb_put(skb, sizeof(*ihdr));
3643d0407baSopenharmony_ci
3653d0407baSopenharmony_ci    skb_set_transport_header(skb, skb->len);
3663d0407baSopenharmony_ci    if (attr->tcp) {
3673d0407baSopenharmony_ci        thdr = (struct tcphdr *)skb_put(skb, sizeof(*thdr));
3683d0407baSopenharmony_ci    } else {
3693d0407baSopenharmony_ci        uhdr = (struct udphdr *)skb_put(skb, sizeof(*uhdr));
3703d0407baSopenharmony_ci    }
3713d0407baSopenharmony_ci
3723d0407baSopenharmony_ci    eth_zero_addr(ehdr->h_source);
3733d0407baSopenharmony_ci    eth_zero_addr(ehdr->h_dest);
3743d0407baSopenharmony_ci
3753d0407baSopenharmony_ci    dwmac_rk_ether_addr_copy(ehdr->h_source, priv->dev->dev_addr);
3763d0407baSopenharmony_ci    dwmac_rk_ether_addr_copy(ehdr->h_dest, attr->dst);
3773d0407baSopenharmony_ci
3783d0407baSopenharmony_ci    ehdr->h_proto = htons(ETH_P_IP);
3793d0407baSopenharmony_ci
3803d0407baSopenharmony_ci    if (attr->tcp) {
3813d0407baSopenharmony_ci        if (!thdr) {
3823d0407baSopenharmony_ci            dev_kfree_skb(skb);
3833d0407baSopenharmony_ci            return NULL;
3843d0407baSopenharmony_ci        }
3853d0407baSopenharmony_ci
3863d0407baSopenharmony_ci        thdr->source = htons(attr->sport);
3873d0407baSopenharmony_ci        thdr->dest = htons(attr->dport);
3883d0407baSopenharmony_ci        thdr->doff = sizeof(struct tcphdr) / 0x04;
3893d0407baSopenharmony_ci        thdr->check = 0;
3903d0407baSopenharmony_ci    } else {
3913d0407baSopenharmony_ci        if (!uhdr) {
3923d0407baSopenharmony_ci            dev_kfree_skb(skb);
3933d0407baSopenharmony_ci            return NULL;
3943d0407baSopenharmony_ci        }
3953d0407baSopenharmony_ci
3963d0407baSopenharmony_ci        uhdr->source = htons(attr->sport);
3973d0407baSopenharmony_ci        uhdr->dest = htons(attr->dport);
3983d0407baSopenharmony_ci        uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
3993d0407baSopenharmony_ci        uhdr->check = 0;
4003d0407baSopenharmony_ci    }
4013d0407baSopenharmony_ci
4023d0407baSopenharmony_ci    ihdr->ihl = 0x05;
4033d0407baSopenharmony_ci    ihdr->ttl = 0x20;
4043d0407baSopenharmony_ci    ihdr->version = 0x04;
4053d0407baSopenharmony_ci    if (attr->tcp) {
4063d0407baSopenharmony_ci        ihdr->protocol = IPPROTO_TCP;
4073d0407baSopenharmony_ci    } else {
4083d0407baSopenharmony_ci        ihdr->protocol = IPPROTO_UDP;
4093d0407baSopenharmony_ci    }
4103d0407baSopenharmony_ci
4113d0407baSopenharmony_ci    iplen = sizeof(*ihdr) + sizeof(*shdr) + attr->size;
4123d0407baSopenharmony_ci    if (attr->tcp) {
4133d0407baSopenharmony_ci        iplen += sizeof(*thdr);
4143d0407baSopenharmony_ci    } else {
4153d0407baSopenharmony_ci        iplen += sizeof(*uhdr);
4163d0407baSopenharmony_ci    }
4173d0407baSopenharmony_ci
4183d0407baSopenharmony_ci    ihdr->tot_len = htons(iplen);
4193d0407baSopenharmony_ci    ihdr->frag_off = 0;
4203d0407baSopenharmony_ci    ihdr->saddr = htonl(attr->ip_src);
4213d0407baSopenharmony_ci    ihdr->daddr = htonl(attr->ip_dst);
4223d0407baSopenharmony_ci    ihdr->tos = 0;
4233d0407baSopenharmony_ci    ihdr->id = 0;
4243d0407baSopenharmony_ci    ip_send_check(ihdr);
4253d0407baSopenharmony_ci
4263d0407baSopenharmony_ci    shdr = (struct dwmac_rk_hdr *)skb_put(skb, sizeof(*shdr));
4273d0407baSopenharmony_ci    shdr->version = 0;
4283d0407baSopenharmony_ci    shdr->magic = cpu_to_be64(DWMAC_RK_TEST_PKT_MAGIC);
4293d0407baSopenharmony_ci    shdr->id = lb_priv->id;
4303d0407baSopenharmony_ci    shdr->tx = lb_priv->tx;
4313d0407baSopenharmony_ci    shdr->rx = lb_priv->rx;
4323d0407baSopenharmony_ci
4333d0407baSopenharmony_ci    if (attr->size) {
4343d0407baSopenharmony_ci        skb_put(skb, attr->size);
4353d0407baSopenharmony_ci        get_random_bytes((u8 *)shdr + sizeof(*shdr), attr->size);
4363d0407baSopenharmony_ci    }
4373d0407baSopenharmony_ci
4383d0407baSopenharmony_ci    skb->csum = 0;
4393d0407baSopenharmony_ci    skb->ip_summed = CHECKSUM_PARTIAL;
4403d0407baSopenharmony_ci    if (attr->tcp) {
4413d0407baSopenharmony_ci        if (!thdr) {
4423d0407baSopenharmony_ci            dev_kfree_skb(skb);
4433d0407baSopenharmony_ci            return NULL;
4443d0407baSopenharmony_ci        }
4453d0407baSopenharmony_ci
4463d0407baSopenharmony_ci        thdr->check = ~tcp_v4_check(skb->len, ihdr->saddr, ihdr->daddr, 0);
4473d0407baSopenharmony_ci        skb->csum_start = skb_transport_header(skb) - skb->head;
4483d0407baSopenharmony_ci        skb->csum_offset = offsetof(struct tcphdr, check);
4493d0407baSopenharmony_ci    } else {
4503d0407baSopenharmony_ci        dwmac_rk_udp4_hwcsum(skb, ihdr->saddr, ihdr->daddr);
4513d0407baSopenharmony_ci    }
4523d0407baSopenharmony_ci
4533d0407baSopenharmony_ci    skb->protocol = htons(ETH_P_IP);
4543d0407baSopenharmony_ci    skb->pkt_type = PACKET_HOST;
4553d0407baSopenharmony_ci
4563d0407baSopenharmony_ci    return skb;
4573d0407baSopenharmony_ci}
4583d0407baSopenharmony_ci
4593d0407baSopenharmony_cistatic int dwmac_rk_loopback_validate(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv, struct sk_buff *skb)
4603d0407baSopenharmony_ci{
4613d0407baSopenharmony_ci    struct dwmac_rk_hdr *shdr;
4623d0407baSopenharmony_ci    struct ethhdr *ehdr;
4633d0407baSopenharmony_ci    struct udphdr *uhdr;
4643d0407baSopenharmony_ci    struct tcphdr *thdr;
4653d0407baSopenharmony_ci    struct iphdr *ihdr;
4663d0407baSopenharmony_ci    int ret = -EAGAIN;
4673d0407baSopenharmony_ci
4683d0407baSopenharmony_ci    if (skb->len >= DWMAC_RK_TEST_PKT_MAX_SIZE) {
4693d0407baSopenharmony_ci        goto out;
4703d0407baSopenharmony_ci    }
4713d0407baSopenharmony_ci
4723d0407baSopenharmony_ci    if (lb_priv->actual_size != skb->len) {
4733d0407baSopenharmony_ci        goto out;
4743d0407baSopenharmony_ci    }
4753d0407baSopenharmony_ci
4763d0407baSopenharmony_ci    ehdr = (struct ethhdr *)(skb->data);
4773d0407baSopenharmony_ci    if (!ether_addr_equal(ehdr->h_dest, lb_priv->packet->dst)) {
4783d0407baSopenharmony_ci        goto out;
4793d0407baSopenharmony_ci    }
4803d0407baSopenharmony_ci
4813d0407baSopenharmony_ci    if (!ether_addr_equal(ehdr->h_source, priv->dev->dev_addr)) {
4823d0407baSopenharmony_ci        goto out;
4833d0407baSopenharmony_ci    }
4843d0407baSopenharmony_ci
4853d0407baSopenharmony_ci    ihdr = (struct iphdr *)(skb->data + ETH_HLEN);
4863d0407baSopenharmony_ci
4873d0407baSopenharmony_ci    if (lb_priv->packet->tcp) {
4883d0407baSopenharmony_ci        if (ihdr->protocol != IPPROTO_TCP) {
4893d0407baSopenharmony_ci            goto out;
4903d0407baSopenharmony_ci        }
4913d0407baSopenharmony_ci
4923d0407baSopenharmony_ci        thdr = (struct tcphdr *)((u8 *)ihdr + 0x04 * ihdr->ihl);
4933d0407baSopenharmony_ci        if (thdr->dest != htons(lb_priv->packet->dport)) {
4943d0407baSopenharmony_ci            goto out;
4953d0407baSopenharmony_ci        }
4963d0407baSopenharmony_ci
4973d0407baSopenharmony_ci        shdr = (struct dwmac_rk_hdr *)((u8 *)thdr + sizeof(*thdr));
4983d0407baSopenharmony_ci    } else {
4993d0407baSopenharmony_ci        if (ihdr->protocol != IPPROTO_UDP) {
5003d0407baSopenharmony_ci            goto out;
5013d0407baSopenharmony_ci        }
5023d0407baSopenharmony_ci
5033d0407baSopenharmony_ci        uhdr = (struct udphdr *)((u8 *)ihdr + 0x04 * ihdr->ihl);
5043d0407baSopenharmony_ci        if (uhdr->dest != htons(lb_priv->packet->dport)) {
5053d0407baSopenharmony_ci            goto out;
5063d0407baSopenharmony_ci        }
5073d0407baSopenharmony_ci
5083d0407baSopenharmony_ci        shdr = (struct dwmac_rk_hdr *)((u8 *)uhdr + sizeof(*uhdr));
5093d0407baSopenharmony_ci    }
5103d0407baSopenharmony_ci
5113d0407baSopenharmony_ci    if (shdr->magic != cpu_to_be64(DWMAC_RK_TEST_PKT_MAGIC)) {
5123d0407baSopenharmony_ci        goto out;
5133d0407baSopenharmony_ci    }
5143d0407baSopenharmony_ci
5153d0407baSopenharmony_ci    if (lb_priv->id != shdr->id) {
5163d0407baSopenharmony_ci        goto out;
5173d0407baSopenharmony_ci    }
5183d0407baSopenharmony_ci
5193d0407baSopenharmony_ci    if (lb_priv->tx != shdr->tx || lb_priv->rx != shdr->rx) {
5203d0407baSopenharmony_ci        goto out;
5213d0407baSopenharmony_ci    }
5223d0407baSopenharmony_ci
5233d0407baSopenharmony_ci    ret = 0;
5243d0407baSopenharmony_ciout:
5253d0407baSopenharmony_ci    return ret;
5263d0407baSopenharmony_ci}
5273d0407baSopenharmony_ci
5283d0407baSopenharmony_cistatic inline int dwmac_rk_rx_fill(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
5293d0407baSopenharmony_ci{
5303d0407baSopenharmony_ci    struct dma_desc *p;
5313d0407baSopenharmony_ci    struct sk_buff *skb;
5323d0407baSopenharmony_ci
5333d0407baSopenharmony_ci    p = lb_priv->dma_rx;
5343d0407baSopenharmony_ci    if (likely(!lb_priv->rx_skbuff)) {
5353d0407baSopenharmony_ci        skb = netdev_alloc_skb_ip_align(priv->dev, lb_priv->buf_sz);
5363d0407baSopenharmony_ci        if (unlikely(!skb)) {
5373d0407baSopenharmony_ci            return -ENOMEM;
5383d0407baSopenharmony_ci        }
5393d0407baSopenharmony_ci
5403d0407baSopenharmony_ci        if (skb_linearize(skb)) {
5413d0407baSopenharmony_ci            pr_err("%s: Rx skb linearize failed\n", __func__);
5423d0407baSopenharmony_ci            lb_priv->rx_skbuff = NULL;
5433d0407baSopenharmony_ci            dev_kfree_skb(skb);
5443d0407baSopenharmony_ci            return -EPERM;
5453d0407baSopenharmony_ci        }
5463d0407baSopenharmony_ci
5473d0407baSopenharmony_ci        lb_priv->rx_skbuff = skb;
5483d0407baSopenharmony_ci        lb_priv->rx_skbuff_dma = dma_map_single(priv->device, skb->data, lb_priv->dma_buf_sz, DMA_FROM_DEVICE);
5493d0407baSopenharmony_ci        if (dma_mapping_error(priv->device, lb_priv->rx_skbuff_dma)) {
5503d0407baSopenharmony_ci            pr_err("%s: Rx dma map failed\n", __func__);
5513d0407baSopenharmony_ci            lb_priv->rx_skbuff = NULL;
5523d0407baSopenharmony_ci            dev_kfree_skb(skb);
5533d0407baSopenharmony_ci            return -EFAULT;
5543d0407baSopenharmony_ci        }
5553d0407baSopenharmony_ci
5563d0407baSopenharmony_ci        stmmac_set_desc_addr(priv, p, lb_priv->rx_skbuff_dma);
5573d0407baSopenharmony_ci        /* Fill DES3 in case of RING mode */
5583d0407baSopenharmony_ci        if (lb_priv->dma_buf_sz == BUF_SIZE_16KiB) {
5593d0407baSopenharmony_ci            p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB);
5603d0407baSopenharmony_ci        }
5613d0407baSopenharmony_ci    }
5623d0407baSopenharmony_ci
5633d0407baSopenharmony_ci    wmb();
5643d0407baSopenharmony_ci    stmmac_set_rx_owner(priv, p, priv->use_riwt);
5653d0407baSopenharmony_ci    wmb();
5663d0407baSopenharmony_ci
5673d0407baSopenharmony_ci    stmmac_set_rx_tail_ptr(priv, priv->ioaddr, lb_priv->rx_tail_addr, 0);
5683d0407baSopenharmony_ci
5693d0407baSopenharmony_ci    return 0;
5703d0407baSopenharmony_ci}
5713d0407baSopenharmony_ci
5723d0407baSopenharmony_cistatic void dwmac_rk_rx_clean(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
5733d0407baSopenharmony_ci{
5743d0407baSopenharmony_ci    struct sk_buff *skb;
5753d0407baSopenharmony_ci
5763d0407baSopenharmony_ci    skb = lb_priv->rx_skbuff;
5773d0407baSopenharmony_ci
5783d0407baSopenharmony_ci    if (likely(lb_priv->rx_skbuff)) {
5793d0407baSopenharmony_ci        dma_unmap_single(priv->device, lb_priv->rx_skbuff_dma, lb_priv->dma_buf_sz, DMA_FROM_DEVICE);
5803d0407baSopenharmony_ci        dev_kfree_skb(skb);
5813d0407baSopenharmony_ci        lb_priv->rx_skbuff = NULL;
5823d0407baSopenharmony_ci    }
5833d0407baSopenharmony_ci}
5843d0407baSopenharmony_ci
5853d0407baSopenharmony_cistatic int dwmac_rk_rx_validate(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
5863d0407baSopenharmony_ci{
5873d0407baSopenharmony_ci    struct dma_desc *p;
5883d0407baSopenharmony_ci    struct sk_buff *skb;
5893d0407baSopenharmony_ci    int coe = priv->hw->rx_csum;
5903d0407baSopenharmony_ci    unsigned int frame_len;
5913d0407baSopenharmony_ci    int ret;
5923d0407baSopenharmony_ci
5933d0407baSopenharmony_ci    p = lb_priv->dma_rx;
5943d0407baSopenharmony_ci    skb = lb_priv->rx_skbuff;
5953d0407baSopenharmony_ci    if (unlikely(!skb)) {
5963d0407baSopenharmony_ci        pr_err("%s: Inconsistent Rx descriptor chain\n", __func__);
5973d0407baSopenharmony_ci        return -EINVAL;
5983d0407baSopenharmony_ci    }
5993d0407baSopenharmony_ci
6003d0407baSopenharmony_ci    frame_len = priv->hw->desc->get_rx_frame_len(p, coe);
6013d0407baSopenharmony_ci    /*  check if frame_len fits the preallocated memory */
6023d0407baSopenharmony_ci    if (frame_len > lb_priv->dma_buf_sz) {
6033d0407baSopenharmony_ci        pr_err("%s: frame_len long: %d\n", __func__, frame_len);
6043d0407baSopenharmony_ci        return -ENOMEM;
6053d0407baSopenharmony_ci    }
6063d0407baSopenharmony_ci
6073d0407baSopenharmony_ci    frame_len -= ETH_FCS_LEN;
6083d0407baSopenharmony_ci    prefetch(skb->data - NET_IP_ALIGN);
6093d0407baSopenharmony_ci    skb_put(skb, frame_len);
6103d0407baSopenharmony_ci    dma_unmap_single(priv->device, lb_priv->rx_skbuff_dma, lb_priv->dma_buf_sz, DMA_FROM_DEVICE);
6113d0407baSopenharmony_ci
6123d0407baSopenharmony_ci    ret = dwmac_rk_loopback_validate(priv, lb_priv, skb);
6133d0407baSopenharmony_ci    dwmac_rk_rx_clean(priv, lb_priv);
6143d0407baSopenharmony_ci    dwmac_rk_rx_fill(priv, lb_priv);
6153d0407baSopenharmony_ci
6163d0407baSopenharmony_ci    return ret;
6173d0407baSopenharmony_ci}
6183d0407baSopenharmony_ci
6193d0407baSopenharmony_cistatic int dwmac_rk_get_desc_status(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
6203d0407baSopenharmony_ci{
6213d0407baSopenharmony_ci    struct dma_desc *txp, *rxp;
6223d0407baSopenharmony_ci    int tx_status, rx_status;
6233d0407baSopenharmony_ci
6243d0407baSopenharmony_ci    txp = lb_priv->dma_tx;
6253d0407baSopenharmony_ci    tx_status = priv->hw->desc->tx_status(&priv->dev->stats, &priv->xstats, txp, priv->ioaddr);
6263d0407baSopenharmony_ci    /* Check if the descriptor is owned by the DMA */
6273d0407baSopenharmony_ci    if (unlikely(tx_status & tx_dma_own)) {
6283d0407baSopenharmony_ci        return -EBUSY;
6293d0407baSopenharmony_ci    }
6303d0407baSopenharmony_ci
6313d0407baSopenharmony_ci    rxp = lb_priv->dma_rx;
6323d0407baSopenharmony_ci    /* read the status of the incoming frame */
6333d0407baSopenharmony_ci    rx_status = priv->hw->desc->rx_status(&priv->dev->stats, &priv->xstats, rxp);
6343d0407baSopenharmony_ci    if (unlikely(rx_status & dma_own)) {
6353d0407baSopenharmony_ci        return -EBUSY;
6363d0407baSopenharmony_ci    }
6373d0407baSopenharmony_ci
6383d0407baSopenharmony_ci    usleep_range(0x64, 0x96);
6393d0407baSopenharmony_ci
6403d0407baSopenharmony_ci    return 0;
6413d0407baSopenharmony_ci}
6423d0407baSopenharmony_ci
6433d0407baSopenharmony_cistatic void dwmac_rk_tx_clean(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
6443d0407baSopenharmony_ci{
6453d0407baSopenharmony_ci    struct sk_buff *skb;
6463d0407baSopenharmony_ci    struct dma_desc *p;
6473d0407baSopenharmony_ci
6483d0407baSopenharmony_ci    skb = lb_priv->tx_skbuff;
6493d0407baSopenharmony_ci    p = lb_priv->dma_tx;
6503d0407baSopenharmony_ci
6513d0407baSopenharmony_ci    if (likely(lb_priv->tx_skbuff_dma)) {
6523d0407baSopenharmony_ci        dma_unmap_single(priv->device, lb_priv->tx_skbuff_dma, lb_priv->tx_skbuff_dma_len, DMA_TO_DEVICE);
6533d0407baSopenharmony_ci        lb_priv->tx_skbuff_dma = 0;
6543d0407baSopenharmony_ci    }
6553d0407baSopenharmony_ci
6563d0407baSopenharmony_ci    if (likely(skb)) {
6573d0407baSopenharmony_ci        dev_kfree_skb(skb);
6583d0407baSopenharmony_ci        lb_priv->tx_skbuff = NULL;
6593d0407baSopenharmony_ci    }
6603d0407baSopenharmony_ci
6613d0407baSopenharmony_ci    priv->hw->desc->release_tx_desc(p, priv->mode);
6623d0407baSopenharmony_ci}
6633d0407baSopenharmony_ci
6643d0407baSopenharmony_cistatic int dwmac_rk_xmit(struct sk_buff *skb, struct net_device *dev, struct dwmac_rk_lb_priv *lb_priv)
6653d0407baSopenharmony_ci{
6663d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(dev);
6673d0407baSopenharmony_ci    unsigned int nopaged_len = skb_headlen(skb);
6683d0407baSopenharmony_ci    int csum_insertion = 0;
6693d0407baSopenharmony_ci    struct dma_desc *desc;
6703d0407baSopenharmony_ci    unsigned int des;
6713d0407baSopenharmony_ci
6723d0407baSopenharmony_ci    priv->hw->mac->reset_eee_mode(priv->hw);
6733d0407baSopenharmony_ci
6743d0407baSopenharmony_ci    csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
6753d0407baSopenharmony_ci
6763d0407baSopenharmony_ci    desc = lb_priv->dma_tx;
6773d0407baSopenharmony_ci    lb_priv->tx_skbuff = skb;
6783d0407baSopenharmony_ci
6793d0407baSopenharmony_ci    des = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE);
6803d0407baSopenharmony_ci    if (dma_mapping_error(priv->device, des)) {
6813d0407baSopenharmony_ci        goto dma_map_err;
6823d0407baSopenharmony_ci    }
6833d0407baSopenharmony_ci
6843d0407baSopenharmony_ci    stmmac_set_desc_addr(priv, desc, des);
6853d0407baSopenharmony_ci    lb_priv->tx_skbuff_dma_len = nopaged_len;
6863d0407baSopenharmony_ci
6873d0407baSopenharmony_ci    /* Prepare the first descriptor setting the OWN bit too */
6883d0407baSopenharmony_ci    stmmac_prepare_tx_desc(priv, desc, 1, nopaged_len, csum_insertion, priv->mode, 1, 1, skb->len);
6893d0407baSopenharmony_ci    stmmac_enable_dma_transmission(priv, priv->ioaddr);
6903d0407baSopenharmony_ci
6913d0407baSopenharmony_ci    lb_priv->tx_tail_addr = lb_priv->dma_tx_phy + sizeof(*desc);
6923d0407baSopenharmony_ci    stmmac_set_tx_tail_ptr(priv, priv->ioaddr, lb_priv->tx_tail_addr, 0);
6933d0407baSopenharmony_ci
6943d0407baSopenharmony_ci    return 0;
6953d0407baSopenharmony_ci
6963d0407baSopenharmony_cidma_map_err:
6973d0407baSopenharmony_ci    pr_err("%s: Tx dma map failed\n", __func__);
6983d0407baSopenharmony_ci    dev_kfree_skb(skb);
6993d0407baSopenharmony_ci    return -EFAULT;
7003d0407baSopenharmony_ci}
7013d0407baSopenharmony_ci
7023d0407baSopenharmony_cistatic int dwmac_rk_loopback_run_first(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
7033d0407baSopenharmony_ci{
7043d0407baSopenharmony_ci    u32 rx_channels_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
7053d0407baSopenharmony_ci    u32 tx_channels_count = min_t(u32, priv->plat->tx_queues_to_use, 1);
7063d0407baSopenharmony_ci    struct sk_buff *tx_skb;
7073d0407baSopenharmony_ci    u32 chan = 0;
7083d0407baSopenharmony_ci    int ret = -EIO, delay;
7093d0407baSopenharmony_ci    u32 status;
7103d0407baSopenharmony_ci    bool finish = false;
7113d0407baSopenharmony_ci
7123d0407baSopenharmony_ci    if (lb_priv->speed == LOOPBACK_SPEED1000) {
7133d0407baSopenharmony_ci        delay = 0x0A;
7143d0407baSopenharmony_ci    } else if (lb_priv->speed == LOOPBACK_SPEED100) {
7153d0407baSopenharmony_ci        delay = 0x14;
7163d0407baSopenharmony_ci    } else if (lb_priv->speed == LOOPBACK_SPEED10) {
7173d0407baSopenharmony_ci        delay = 0x32;
7183d0407baSopenharmony_ci    } else {
7193d0407baSopenharmony_ci        return -EPERM;
7203d0407baSopenharmony_ci    }
7213d0407baSopenharmony_ci
7223d0407baSopenharmony_ci    if (dwmac_rk_rx_fill(priv, lb_priv)) {
7233d0407baSopenharmony_ci        return -ENOMEM;
7243d0407baSopenharmony_ci    }
7253d0407baSopenharmony_ci
7263d0407baSopenharmony_ci    /* Enable the MAC Rx/Tx */
7273d0407baSopenharmony_ci    stmmac_mac_set(priv, priv->ioaddr, true);
7283d0407baSopenharmony_ci
7293d0407baSopenharmony_ci    for (chan = 0; chan < rx_channels_count; chan++) {
7303d0407baSopenharmony_ci        stmmac_start_rx(priv, priv->ioaddr, chan);
7313d0407baSopenharmony_ci    }
7323d0407baSopenharmony_ci    for (chan = 0; chan < tx_channels_count; chan++) {
7333d0407baSopenharmony_ci        stmmac_start_tx(priv, priv->ioaddr, chan);
7343d0407baSopenharmony_ci    }
7353d0407baSopenharmony_ci
7363d0407baSopenharmony_ci    tx_skb = dwmac_rk_get_skb(priv, lb_priv);
7373d0407baSopenharmony_ci    if (!tx_skb) {
7383d0407baSopenharmony_ci        ret = -ENOMEM;
7393d0407baSopenharmony_ci        goto stop;
7403d0407baSopenharmony_ci    }
7413d0407baSopenharmony_ci
7423d0407baSopenharmony_ci    if (dwmac_rk_xmit(tx_skb, priv->dev, lb_priv)) {
7433d0407baSopenharmony_ci        ret = -EFAULT;
7443d0407baSopenharmony_ci        goto stop;
7453d0407baSopenharmony_ci    }
7463d0407baSopenharmony_ci
7473d0407baSopenharmony_ci    do {
7483d0407baSopenharmony_ci        usleep_range(0x64, 0x96);
7493d0407baSopenharmony_ci        delay--;
7503d0407baSopenharmony_ci        if (priv->plat->has_gmac4) {
7513d0407baSopenharmony_ci            status = readl(priv->ioaddr + DMA_CHAN_STATUS(0));
7523d0407baSopenharmony_ci            finish = (status & DMA_CHAN_STATUS_ERI) && (status & DMA_CHAN_STATUS_ETI);
7533d0407baSopenharmony_ci        } else {
7543d0407baSopenharmony_ci            status = readl(priv->ioaddr + DMA_STATUS);
7553d0407baSopenharmony_ci            finish = (status & DMA_STATUS_ERI) && (status & DMA_STATUS_ETI);
7563d0407baSopenharmony_ci        }
7573d0407baSopenharmony_ci
7583d0407baSopenharmony_ci        if (finish) {
7593d0407baSopenharmony_ci            if (!dwmac_rk_get_desc_status(priv, lb_priv)) {
7603d0407baSopenharmony_ci                ret = dwmac_rk_rx_validate(priv, lb_priv);
7613d0407baSopenharmony_ci                break;
7623d0407baSopenharmony_ci            }
7633d0407baSopenharmony_ci        }
7643d0407baSopenharmony_ci    } while (delay <= 0);
7653d0407baSopenharmony_ci    writel((status & 0x1ffff), priv->ioaddr + DMA_STATUS);
7663d0407baSopenharmony_ci
7673d0407baSopenharmony_cistop:
7683d0407baSopenharmony_ci    for (chan = 0; chan < rx_channels_count; chan++) {
7693d0407baSopenharmony_ci        stmmac_stop_rx(priv, priv->ioaddr, chan);
7703d0407baSopenharmony_ci    }
7713d0407baSopenharmony_ci    for (chan = 0; chan < tx_channels_count; chan++) {
7723d0407baSopenharmony_ci        stmmac_stop_tx(priv, priv->ioaddr, chan);
7733d0407baSopenharmony_ci    }
7743d0407baSopenharmony_ci
7753d0407baSopenharmony_ci    stmmac_mac_set(priv, priv->ioaddr, false);
7763d0407baSopenharmony_ci    /* wait for state machine is disabled */
7773d0407baSopenharmony_ci    usleep_range(0x64, 0x96);
7783d0407baSopenharmony_ci
7793d0407baSopenharmony_ci    dwmac_rk_tx_clean(priv, lb_priv);
7803d0407baSopenharmony_ci    dwmac_rk_rx_clean(priv, lb_priv);
7813d0407baSopenharmony_ci
7823d0407baSopenharmony_ci    return ret;
7833d0407baSopenharmony_ci}
7843d0407baSopenharmony_ci
7853d0407baSopenharmony_cistatic int dwmac_rk_loopback_with_identify(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv, int tx, int rx)
7863d0407baSopenharmony_ci{
7873d0407baSopenharmony_ci    lb_priv->id++;
7883d0407baSopenharmony_ci    lb_priv->tx = tx;
7893d0407baSopenharmony_ci    lb_priv->rx = rx;
7903d0407baSopenharmony_ci
7913d0407baSopenharmony_ci    lb_priv->packet = &dwmac_rk_tcp_attr;
7923d0407baSopenharmony_ci    dwmac_rk_set_rgmii_delayline(priv, tx, rx);
7933d0407baSopenharmony_ci
7943d0407baSopenharmony_ci    return dwmac_rk_loopback_run_first(priv, lb_priv);
7953d0407baSopenharmony_ci}
7963d0407baSopenharmony_ci
7973d0407baSopenharmony_cistatic inline bool dwmac_rk_delayline_is_txvalid(struct dwmac_rk_lb_priv *lb_priv, int tx)
7983d0407baSopenharmony_ci{
7993d0407baSopenharmony_ci    if (tx > 0 && tx < lb_priv->max_delay) {
8003d0407baSopenharmony_ci        return true;
8013d0407baSopenharmony_ci    } else {
8023d0407baSopenharmony_ci        return false;
8033d0407baSopenharmony_ci    }
8043d0407baSopenharmony_ci}
8053d0407baSopenharmony_ci
8063d0407baSopenharmony_cistatic inline bool dwmac_rk_delayline_is_valid(struct dwmac_rk_lb_priv *lb_priv, int tx, int rx)
8073d0407baSopenharmony_ci{
8083d0407baSopenharmony_ci    if ((tx > 0 && tx < lb_priv->max_delay) && (rx > 0 && rx < lb_priv->max_delay)) {
8093d0407baSopenharmony_ci        return true;
8103d0407baSopenharmony_ci    } else {
8113d0407baSopenharmony_ci        return false;
8123d0407baSopenharmony_ci    }
8133d0407baSopenharmony_ci}
8143d0407baSopenharmony_ci
8153d0407baSopenharmony_cistatic int dwmac_rk_delayline_scan_cross(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
8163d0407baSopenharmony_ci{
8173d0407baSopenharmony_ci    int tx_left, tx_right, rx_up, rx_down;
8183d0407baSopenharmony_ci    int i, j, tx_index, rx_index;
8193d0407baSopenharmony_ci    int tx_mid = 0, rx_mid = 0;
8203d0407baSopenharmony_ci
8213d0407baSopenharmony_ci    /* initiation */
8223d0407baSopenharmony_ci    tx_index = SCAN_STEP;
8233d0407baSopenharmony_ci    rx_index = SCAN_STEP;
8243d0407baSopenharmony_ci
8253d0407baSopenharmony_ci    while (1) {
8263d0407baSopenharmony_ci        /* start from rx based on the experience */
8273d0407baSopenharmony_ci        for (i = rx_index; i <= (lb_priv->max_delay - SCAN_STEP); i += SCAN_STEP) {
8283d0407baSopenharmony_ci            tx_left = 0;
8293d0407baSopenharmony_ci            tx_right = 0;
8303d0407baSopenharmony_ci            tx_mid = 0;
8313d0407baSopenharmony_ci
8323d0407baSopenharmony_ci            for (j = tx_index; j <= (lb_priv->max_delay - SCAN_STEP); j += SCAN_STEP) {
8333d0407baSopenharmony_ci                if (!dwmac_rk_loopback_with_identify(priv, lb_priv, j, i)) {
8343d0407baSopenharmony_ci                    if (!tx_left) {
8353d0407baSopenharmony_ci                        tx_left = j;
8363d0407baSopenharmony_ci                    }
8373d0407baSopenharmony_ci                    tx_right = j;
8383d0407baSopenharmony_ci                }
8393d0407baSopenharmony_ci            }
8403d0407baSopenharmony_ci
8413d0407baSopenharmony_ci            /* look for tx_mid */
8423d0407baSopenharmony_ci            if ((tx_right - tx_left) > SCAN_VALID_RANGE) {
8433d0407baSopenharmony_ci                tx_mid = (tx_right + tx_left) / 0x02;
8443d0407baSopenharmony_ci                break;
8453d0407baSopenharmony_ci            }
8463d0407baSopenharmony_ci        }
8473d0407baSopenharmony_ci
8483d0407baSopenharmony_ci        /* Worst case: reach the end */
8493d0407baSopenharmony_ci        if (i >= (lb_priv->max_delay - SCAN_STEP)) {
8503d0407baSopenharmony_ci            goto end;
8513d0407baSopenharmony_ci        }
8523d0407baSopenharmony_ci
8533d0407baSopenharmony_ci        rx_up = 0;
8543d0407baSopenharmony_ci        rx_down = 0;
8553d0407baSopenharmony_ci
8563d0407baSopenharmony_ci        /* look for rx_mid base on the tx_mid */
8573d0407baSopenharmony_ci        for (i = SCAN_STEP; i <= (lb_priv->max_delay - SCAN_STEP); i += SCAN_STEP) {
8583d0407baSopenharmony_ci            if (!dwmac_rk_loopback_with_identify(priv, lb_priv, tx_mid, i)) {
8593d0407baSopenharmony_ci                if (!rx_up) {
8603d0407baSopenharmony_ci                    rx_up = i;
8613d0407baSopenharmony_ci                }
8623d0407baSopenharmony_ci                rx_down = i;
8633d0407baSopenharmony_ci            }
8643d0407baSopenharmony_ci        }
8653d0407baSopenharmony_ci
8663d0407baSopenharmony_ci        if ((rx_down - rx_up) > SCAN_VALID_RANGE) {
8673d0407baSopenharmony_ci            /* Now get the rx_mid */
8683d0407baSopenharmony_ci            rx_mid = (rx_up + rx_down) / 0x02;
8693d0407baSopenharmony_ci        } else {
8703d0407baSopenharmony_ci            rx_index += SCAN_STEP;
8713d0407baSopenharmony_ci            rx_mid = 0;
8723d0407baSopenharmony_ci            continue;
8733d0407baSopenharmony_ci        }
8743d0407baSopenharmony_ci
8753d0407baSopenharmony_ci        if (dwmac_rk_delayline_is_valid(lb_priv, tx_mid, rx_mid)) {
8763d0407baSopenharmony_ci            lb_priv->final_tx = tx_mid;
8773d0407baSopenharmony_ci            lb_priv->final_rx = rx_mid;
8783d0407baSopenharmony_ci
8793d0407baSopenharmony_ci            pr_info("Find available tx_delay = 0x%02x, rx_delay = 0x%02x\n", lb_priv->final_tx, lb_priv->final_rx);
8803d0407baSopenharmony_ci
8813d0407baSopenharmony_ci            return 0;
8823d0407baSopenharmony_ci        }
8833d0407baSopenharmony_ci        break;
8843d0407baSopenharmony_ci    }
8853d0407baSopenharmony_ciend:
8863d0407baSopenharmony_ci    pr_err("Can't find available delayline\n");
8873d0407baSopenharmony_ci    return -ENXIO;
8883d0407baSopenharmony_ci}
8893d0407baSopenharmony_ci
8903d0407baSopenharmony_cistatic int dwmac_rk_delayline_scan(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
8913d0407baSopenharmony_ci{
8923d0407baSopenharmony_ci    int phy_iface = dwmac_rk_get_phy_interface(priv);
8933d0407baSopenharmony_ci    int tx, rx, tx_sum, rx_sum, count;
8943d0407baSopenharmony_ci    int tx_mid, rx_mid;
8953d0407baSopenharmony_ci    int ret = -ENXIO;
8963d0407baSopenharmony_ci
8973d0407baSopenharmony_ci    tx_sum = 0;
8983d0407baSopenharmony_ci    rx_sum = 0;
8993d0407baSopenharmony_ci    count = 0;
9003d0407baSopenharmony_ci
9013d0407baSopenharmony_ci    for (rx = 0x0; rx <= lb_priv->max_delay; rx++) {
9023d0407baSopenharmony_ci        if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID) {
9033d0407baSopenharmony_ci            rx = -1;
9043d0407baSopenharmony_ci        }
9053d0407baSopenharmony_ci        printk(KERN_CONT "RX(%03d):", rx);
9063d0407baSopenharmony_ci        for (tx = 0x0; tx <= lb_priv->max_delay; tx++) {
9073d0407baSopenharmony_ci            if (!dwmac_rk_loopback_with_identify(priv, lb_priv, tx, rx)) {
9083d0407baSopenharmony_ci                tx_sum += tx;
9093d0407baSopenharmony_ci                rx_sum += rx;
9103d0407baSopenharmony_ci                count++;
9113d0407baSopenharmony_ci                printk(KERN_CONT "O");
9123d0407baSopenharmony_ci            } else {
9133d0407baSopenharmony_ci                printk(KERN_CONT " ");
9143d0407baSopenharmony_ci            }
9153d0407baSopenharmony_ci        }
9163d0407baSopenharmony_ci        printk(KERN_CONT "\n");
9173d0407baSopenharmony_ci
9183d0407baSopenharmony_ci        if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID) {
9193d0407baSopenharmony_ci            break;
9203d0407baSopenharmony_ci        }
9213d0407baSopenharmony_ci    }
9223d0407baSopenharmony_ci
9233d0407baSopenharmony_ci    if (tx_sum && rx_sum && count) {
9243d0407baSopenharmony_ci        tx_mid = tx_sum / count;
9253d0407baSopenharmony_ci        rx_mid = rx_sum / count;
9263d0407baSopenharmony_ci
9273d0407baSopenharmony_ci        if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID) {
9283d0407baSopenharmony_ci            if (dwmac_rk_delayline_is_txvalid(lb_priv, tx_mid)) {
9293d0407baSopenharmony_ci                lb_priv->final_tx = tx_mid;
9303d0407baSopenharmony_ci                lb_priv->final_rx = -1;
9313d0407baSopenharmony_ci                ret = 0;
9323d0407baSopenharmony_ci            }
9333d0407baSopenharmony_ci        } else {
9343d0407baSopenharmony_ci            if (dwmac_rk_delayline_is_valid(lb_priv, tx_mid, rx_mid)) {
9353d0407baSopenharmony_ci                lb_priv->final_tx = tx_mid;
9363d0407baSopenharmony_ci                lb_priv->final_rx = rx_mid;
9373d0407baSopenharmony_ci                ret = 0;
9383d0407baSopenharmony_ci            }
9393d0407baSopenharmony_ci        }
9403d0407baSopenharmony_ci    }
9413d0407baSopenharmony_ci
9423d0407baSopenharmony_ci    if (ret) {
9433d0407baSopenharmony_ci        pr_err("\nCan't find suitable delayline\n");
9443d0407baSopenharmony_ci    } else {
9453d0407baSopenharmony_ci        if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID) {
9463d0407baSopenharmony_ci            pr_info("Find available tx_delay = 0x%02x, rx_delay = disable\n", lb_priv->final_tx);
9473d0407baSopenharmony_ci        } else {
9483d0407baSopenharmony_ci            pr_info("\nFind suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n", lb_priv->final_tx, lb_priv->final_rx);
9493d0407baSopenharmony_ci        }
9503d0407baSopenharmony_ci    }
9513d0407baSopenharmony_ci
9523d0407baSopenharmony_ci    return ret;
9533d0407baSopenharmony_ci}
9543d0407baSopenharmony_ci
9553d0407baSopenharmony_cistatic int dwmac_rk_loopback_delayline_scan(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
9563d0407baSopenharmony_ci{
9573d0407baSopenharmony_ci    if (lb_priv->sysfs) {
9583d0407baSopenharmony_ci        return dwmac_rk_delayline_scan(priv, lb_priv);
9593d0407baSopenharmony_ci    } else {
9603d0407baSopenharmony_ci        return dwmac_rk_delayline_scan_cross(priv, lb_priv);
9613d0407baSopenharmony_ci    }
9623d0407baSopenharmony_ci}
9633d0407baSopenharmony_ci
9643d0407baSopenharmony_cistatic void dwmac_rk_dma_free_rx_skbufs(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
9653d0407baSopenharmony_ci{
9663d0407baSopenharmony_ci    if (lb_priv->rx_skbuff) {
9673d0407baSopenharmony_ci        dma_unmap_single(priv->device, lb_priv->rx_skbuff_dma, lb_priv->dma_buf_sz, DMA_FROM_DEVICE);
9683d0407baSopenharmony_ci        dev_kfree_skb_any(lb_priv->rx_skbuff);
9693d0407baSopenharmony_ci    }
9703d0407baSopenharmony_ci    lb_priv->rx_skbuff = NULL;
9713d0407baSopenharmony_ci}
9723d0407baSopenharmony_ci
9733d0407baSopenharmony_cistatic void dwmac_rk_dma_free_tx_skbufs(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
9743d0407baSopenharmony_ci{
9753d0407baSopenharmony_ci    if (lb_priv->tx_skbuff_dma) {
9763d0407baSopenharmony_ci        dma_unmap_single(priv->device, lb_priv->tx_skbuff_dma, lb_priv->tx_skbuff_dma_len, DMA_TO_DEVICE);
9773d0407baSopenharmony_ci    }
9783d0407baSopenharmony_ci
9793d0407baSopenharmony_ci    if (lb_priv->tx_skbuff) {
9803d0407baSopenharmony_ci        dev_kfree_skb_any(lb_priv->tx_skbuff);
9813d0407baSopenharmony_ci        lb_priv->tx_skbuff = NULL;
9823d0407baSopenharmony_ci        lb_priv->tx_skbuff_dma = 0;
9833d0407baSopenharmony_ci    }
9843d0407baSopenharmony_ci}
9853d0407baSopenharmony_ci
9863d0407baSopenharmony_cistatic int dwmac_rk_init_dma_desc_rings(struct net_device *dev, gfp_t flags, struct dwmac_rk_lb_priv *lb_priv)
9873d0407baSopenharmony_ci{
9883d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(dev);
9893d0407baSopenharmony_ci    struct dma_desc *p;
9903d0407baSopenharmony_ci
9913d0407baSopenharmony_ci    p = lb_priv->dma_tx;
9923d0407baSopenharmony_ci    p->des2 = 0;
9933d0407baSopenharmony_ci    lb_priv->tx_skbuff_dma = 0;
9943d0407baSopenharmony_ci    lb_priv->tx_skbuff_dma_len = 0;
9953d0407baSopenharmony_ci    lb_priv->tx_skbuff = NULL;
9963d0407baSopenharmony_ci
9973d0407baSopenharmony_ci    lb_priv->rx_skbuff = NULL;
9983d0407baSopenharmony_ci    stmmac_init_rx_desc(priv, lb_priv->dma_rx, priv->use_riwt, priv->mode, true, lb_priv->dma_buf_sz);
9993d0407baSopenharmony_ci
10003d0407baSopenharmony_ci    stmmac_init_tx_desc(priv, lb_priv->dma_tx, priv->mode, true);
10013d0407baSopenharmony_ci
10023d0407baSopenharmony_ci    return 0;
10033d0407baSopenharmony_ci}
10043d0407baSopenharmony_ci
10053d0407baSopenharmony_cistatic int dwmac_rk_alloc_dma_desc_resources(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
10063d0407baSopenharmony_ci{
10073d0407baSopenharmony_ci    int ret = -ENOMEM;
10083d0407baSopenharmony_ci
10093d0407baSopenharmony_ci    /* desc dma map */
10103d0407baSopenharmony_ci    lb_priv->dma_rx = dma_alloc_coherent(priv->device, sizeof(struct dma_desc), &lb_priv->dma_rx_phy, GFP_KERNEL);
10113d0407baSopenharmony_ci    if (!lb_priv->dma_rx) {
10123d0407baSopenharmony_ci        return ret;
10133d0407baSopenharmony_ci    }
10143d0407baSopenharmony_ci
10153d0407baSopenharmony_ci    lb_priv->dma_tx = dma_alloc_coherent(priv->device, sizeof(struct dma_desc), &lb_priv->dma_tx_phy, GFP_KERNEL);
10163d0407baSopenharmony_ci    if (!lb_priv->dma_tx) {
10173d0407baSopenharmony_ci        dma_free_coherent(priv->device, sizeof(struct dma_desc), lb_priv->dma_rx, lb_priv->dma_rx_phy);
10183d0407baSopenharmony_ci        return ret;
10193d0407baSopenharmony_ci    }
10203d0407baSopenharmony_ci
10213d0407baSopenharmony_ci    return 0;
10223d0407baSopenharmony_ci}
10233d0407baSopenharmony_ci
10243d0407baSopenharmony_cistatic void dwmac_rk_free_dma_desc_resources(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
10253d0407baSopenharmony_ci{
10263d0407baSopenharmony_ci    /* Release the DMA TX/RX socket buffers */
10273d0407baSopenharmony_ci    dwmac_rk_dma_free_rx_skbufs(priv, lb_priv);
10283d0407baSopenharmony_ci    dwmac_rk_dma_free_tx_skbufs(priv, lb_priv);
10293d0407baSopenharmony_ci
10303d0407baSopenharmony_ci    dma_free_coherent(priv->device, sizeof(struct dma_desc), lb_priv->dma_tx, lb_priv->dma_tx_phy);
10313d0407baSopenharmony_ci    dma_free_coherent(priv->device, sizeof(struct dma_desc), lb_priv->dma_rx, lb_priv->dma_rx_phy);
10323d0407baSopenharmony_ci}
10333d0407baSopenharmony_ci
10343d0407baSopenharmony_cistatic int dwmac_rk_init_dma_engine(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
10353d0407baSopenharmony_ci{
10363d0407baSopenharmony_ci    u32 rx_channels_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
10373d0407baSopenharmony_ci    u32 tx_channels_count = min_t(u32, priv->plat->tx_queues_to_use, 1);
10383d0407baSopenharmony_ci    u32 dma_csr_ch = max(rx_channels_count, tx_channels_count);
10393d0407baSopenharmony_ci    u32 chan = 0;
10403d0407baSopenharmony_ci    int ret = 0;
10413d0407baSopenharmony_ci
10423d0407baSopenharmony_ci    ret = stmmac_reset(priv, priv->ioaddr);
10433d0407baSopenharmony_ci    if (ret) {
10443d0407baSopenharmony_ci        dev_err(priv->device, "Failed to reset the dma\n");
10453d0407baSopenharmony_ci        return ret;
10463d0407baSopenharmony_ci    }
10473d0407baSopenharmony_ci
10483d0407baSopenharmony_ci    /* DMA Configuration */
10493d0407baSopenharmony_ci    stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg, 0);
10503d0407baSopenharmony_ci
10513d0407baSopenharmony_ci    if (priv->plat->axi) {
10523d0407baSopenharmony_ci        stmmac_axi(priv, priv->ioaddr, priv->plat->axi);
10533d0407baSopenharmony_ci    }
10543d0407baSopenharmony_ci
10553d0407baSopenharmony_ci    for (chan = 0; chan < dma_csr_ch; chan++) {
10563d0407baSopenharmony_ci        stmmac_init_chan(priv, priv->ioaddr, priv->plat->dma_cfg, 0);
10573d0407baSopenharmony_ci    }
10583d0407baSopenharmony_ci
10593d0407baSopenharmony_ci    /* DMA RX Channel Configuration */
10603d0407baSopenharmony_ci    for (chan = 0; chan < rx_channels_count; chan++) {
10613d0407baSopenharmony_ci        stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, lb_priv->dma_rx_phy, 0);
10623d0407baSopenharmony_ci
10633d0407baSopenharmony_ci        lb_priv->rx_tail_addr = lb_priv->dma_rx_phy + (1 * sizeof(struct dma_desc));
10643d0407baSopenharmony_ci        stmmac_set_rx_tail_ptr(priv, priv->ioaddr, lb_priv->rx_tail_addr, 0);
10653d0407baSopenharmony_ci    }
10663d0407baSopenharmony_ci
10673d0407baSopenharmony_ci    /* DMA TX Channel Configuration */
10683d0407baSopenharmony_ci    for (chan = 0; chan < tx_channels_count; chan++) {
10693d0407baSopenharmony_ci        stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, lb_priv->dma_tx_phy, chan);
10703d0407baSopenharmony_ci
10713d0407baSopenharmony_ci        lb_priv->tx_tail_addr = lb_priv->dma_tx_phy;
10723d0407baSopenharmony_ci        stmmac_set_tx_tail_ptr(priv, priv->ioaddr, lb_priv->tx_tail_addr, chan);
10733d0407baSopenharmony_ci    }
10743d0407baSopenharmony_ci
10753d0407baSopenharmony_ci    return ret;
10763d0407baSopenharmony_ci}
10773d0407baSopenharmony_ci
10783d0407baSopenharmony_cistatic void dwmac_rk_dma_operation_mode(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
10793d0407baSopenharmony_ci{
10803d0407baSopenharmony_ci    u32 rx_channels_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
10813d0407baSopenharmony_ci    u32 tx_channels_count = min_t(u32, priv->plat->tx_queues_to_use, 1);
10823d0407baSopenharmony_ci    int rxfifosz = priv->plat->rx_fifo_size;
10833d0407baSopenharmony_ci    int txfifosz = priv->plat->tx_fifo_size;
10843d0407baSopenharmony_ci    u32 txmode = SF_DMA_MODE;
10853d0407baSopenharmony_ci    u32 rxmode = SF_DMA_MODE;
10863d0407baSopenharmony_ci    u32 chan = 0;
10873d0407baSopenharmony_ci    u8 qmode = 0;
10883d0407baSopenharmony_ci
10893d0407baSopenharmony_ci    if (rxfifosz == 0) {
10903d0407baSopenharmony_ci        rxfifosz = priv->dma_cap.rx_fifo_size;
10913d0407baSopenharmony_ci    }
10923d0407baSopenharmony_ci    if (txfifosz == 0) {
10933d0407baSopenharmony_ci        txfifosz = priv->dma_cap.tx_fifo_size;
10943d0407baSopenharmony_ci    }
10953d0407baSopenharmony_ci
10963d0407baSopenharmony_ci    /* Adjust for real per queue fifo size */
10973d0407baSopenharmony_ci    if (rx_channels_count == 0 || tx_channels_count == 0) {
10983d0407baSopenharmony_ci        return;
10993d0407baSopenharmony_ci    }
11003d0407baSopenharmony_ci    rxfifosz /= rx_channels_count;
11013d0407baSopenharmony_ci    txfifosz /= tx_channels_count;
11023d0407baSopenharmony_ci
11033d0407baSopenharmony_ci    /* configure all channels */
11043d0407baSopenharmony_ci    for (chan = 0; chan < rx_channels_count; chan++) {
11053d0407baSopenharmony_ci        qmode = priv->plat->rx_queues_cfg[chan].mode_to_use;
11063d0407baSopenharmony_ci
11073d0407baSopenharmony_ci        stmmac_dma_rx_mode(priv, priv->ioaddr, rxmode, chan, rxfifosz, qmode);
11083d0407baSopenharmony_ci        stmmac_set_dma_bfsize(priv, priv->ioaddr, lb_priv->dma_buf_sz, chan);
11093d0407baSopenharmony_ci    }
11103d0407baSopenharmony_ci
11113d0407baSopenharmony_ci    for (chan = 0; chan < tx_channels_count; chan++) {
11123d0407baSopenharmony_ci        qmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
11133d0407baSopenharmony_ci
11143d0407baSopenharmony_ci        stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan, txfifosz, qmode);
11153d0407baSopenharmony_ci    }
11163d0407baSopenharmony_ci}
11173d0407baSopenharmony_ci
11183d0407baSopenharmony_cistatic void dwmac_rk_rx_queue_dma_chan_map(struct stmmac_priv *priv)
11193d0407baSopenharmony_ci{
11203d0407baSopenharmony_ci    u32 rx_queues_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
11213d0407baSopenharmony_ci    u32 queue;
11223d0407baSopenharmony_ci    u32 chan;
11233d0407baSopenharmony_ci
11243d0407baSopenharmony_ci    for (queue = 0; queue < rx_queues_count; queue++) {
11253d0407baSopenharmony_ci        chan = priv->plat->rx_queues_cfg[queue].chan;
11263d0407baSopenharmony_ci        stmmac_map_mtl_to_dma(priv, priv->hw, queue, chan);
11273d0407baSopenharmony_ci    }
11283d0407baSopenharmony_ci}
11293d0407baSopenharmony_ci
11303d0407baSopenharmony_cistatic void dwmac_rk_mac_enable_rx_queues(struct stmmac_priv *priv)
11313d0407baSopenharmony_ci{
11323d0407baSopenharmony_ci    u32 rx_queues_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
11333d0407baSopenharmony_ci    int queue;
11343d0407baSopenharmony_ci    u8 mode;
11353d0407baSopenharmony_ci
11363d0407baSopenharmony_ci    for (queue = 0; queue < rx_queues_count; queue++) {
11373d0407baSopenharmony_ci        mode = priv->plat->rx_queues_cfg[queue].mode_to_use;
11383d0407baSopenharmony_ci        stmmac_rx_queue_enable(priv, priv->hw, mode, queue);
11393d0407baSopenharmony_ci    }
11403d0407baSopenharmony_ci}
11413d0407baSopenharmony_ci
11423d0407baSopenharmony_cistatic void dwmac_rk_mtl_configuration(struct stmmac_priv *priv)
11433d0407baSopenharmony_ci{
11443d0407baSopenharmony_ci    /* Map RX MTL to DMA channels */
11453d0407baSopenharmony_ci    dwmac_rk_rx_queue_dma_chan_map(priv);
11463d0407baSopenharmony_ci
11473d0407baSopenharmony_ci    /* Enable MAC RX Queues */
11483d0407baSopenharmony_ci    dwmac_rk_mac_enable_rx_queues(priv);
11493d0407baSopenharmony_ci}
11503d0407baSopenharmony_ci
11513d0407baSopenharmony_cistatic void dwmac_rk_mmc_setup(struct stmmac_priv *priv)
11523d0407baSopenharmony_ci{
11533d0407baSopenharmony_ci    unsigned int mode =
11543d0407baSopenharmony_ci        MMC_CNTRL_RESET_ON_READ | MMC_CNTRL_COUNTER_RESET | MMC_CNTRL_PRESET | MMC_CNTRL_FULL_HALF_PRESET;
11553d0407baSopenharmony_ci
11563d0407baSopenharmony_ci    stmmac_mmc_intr_all_mask(priv, priv->mmcaddr);
11573d0407baSopenharmony_ci
11583d0407baSopenharmony_ci    if (priv->dma_cap.rmon) {
11593d0407baSopenharmony_ci        stmmac_mmc_ctrl(priv, priv->mmcaddr, mode);
11603d0407baSopenharmony_ci        memset(&priv->mmc, 0, sizeof(struct stmmac_counters));
11613d0407baSopenharmony_ci    } else {
11623d0407baSopenharmony_ci        netdev_info(priv->dev, "No MAC Management Counters available\n");
11633d0407baSopenharmony_ci    }
11643d0407baSopenharmony_ci}
11653d0407baSopenharmony_ci
11663d0407baSopenharmony_cistatic int dwmac_rk_init(struct net_device *dev, struct dwmac_rk_lb_priv *lb_priv)
11673d0407baSopenharmony_ci{
11683d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(dev);
11693d0407baSopenharmony_ci    int ret;
11703d0407baSopenharmony_ci    u32 mode;
11713d0407baSopenharmony_ci
11723d0407baSopenharmony_ci    lb_priv->dma_buf_sz = 0x600; /* mtu 1500 size */
11733d0407baSopenharmony_ci
11743d0407baSopenharmony_ci    if (priv->plat->has_gmac4) {
11753d0407baSopenharmony_ci        lb_priv->buf_sz = priv->dma_cap.rx_fifo_size; /* rx fifo size */
11763d0407baSopenharmony_ci    } else {
11773d0407baSopenharmony_ci        lb_priv->buf_sz = 0x1000; /* rx fifo size */
11783d0407baSopenharmony_ci    }
11793d0407baSopenharmony_ci
11803d0407baSopenharmony_ci    ret = dwmac_rk_alloc_dma_desc_resources(priv, lb_priv);
11813d0407baSopenharmony_ci    if (ret < 0) {
11823d0407baSopenharmony_ci        pr_err("%s: DMA descriptors allocation failed\n", __func__);
11833d0407baSopenharmony_ci        return ret;
11843d0407baSopenharmony_ci    }
11853d0407baSopenharmony_ci
11863d0407baSopenharmony_ci    ret = dwmac_rk_init_dma_desc_rings(dev, GFP_KERNEL, lb_priv);
11873d0407baSopenharmony_ci    if (ret < 0) {
11883d0407baSopenharmony_ci        pr_err("%s: DMA descriptors initialization failed\n", __func__);
11893d0407baSopenharmony_ci        goto init_error;
11903d0407baSopenharmony_ci    }
11913d0407baSopenharmony_ci
11923d0407baSopenharmony_ci    /* DMA initialization and SW reset */
11933d0407baSopenharmony_ci    ret = dwmac_rk_init_dma_engine(priv, lb_priv);
11943d0407baSopenharmony_ci    if (ret < 0) {
11953d0407baSopenharmony_ci        pr_err("%s: DMA engine initialization failed\n", __func__);
11963d0407baSopenharmony_ci        goto init_error;
11973d0407baSopenharmony_ci    }
11983d0407baSopenharmony_ci
11993d0407baSopenharmony_ci    /* Copy the MAC addr into the HW  */
12003d0407baSopenharmony_ci    priv->hw->mac->set_umac_addr(priv->hw, dev->dev_addr, 0);
12013d0407baSopenharmony_ci
12023d0407baSopenharmony_ci    /* Initialize the MAC Core */
12033d0407baSopenharmony_ci    stmmac_core_init(priv, priv->hw, dev);
12043d0407baSopenharmony_ci
12053d0407baSopenharmony_ci    dwmac_rk_mtl_configuration(priv);
12063d0407baSopenharmony_ci
12073d0407baSopenharmony_ci    dwmac_rk_mmc_setup(priv);
12083d0407baSopenharmony_ci
12093d0407baSopenharmony_ci    ret = priv->hw->mac->rx_ipc(priv->hw);
12103d0407baSopenharmony_ci    if (!ret) {
12113d0407baSopenharmony_ci        pr_warn(" RX IPC Checksum Offload disabled\n");
12123d0407baSopenharmony_ci        priv->plat->rx_coe = STMMAC_RX_COE_NONE;
12133d0407baSopenharmony_ci        priv->hw->rx_csum = 0;
12143d0407baSopenharmony_ci    }
12153d0407baSopenharmony_ci
12163d0407baSopenharmony_ci    /* Set the HW DMA mode and the COE */
12173d0407baSopenharmony_ci    dwmac_rk_dma_operation_mode(priv, lb_priv);
12183d0407baSopenharmony_ci
12193d0407baSopenharmony_ci    if (priv->plat->has_gmac4) {
12203d0407baSopenharmony_ci        mode = readl(priv->ioaddr + DMA_CHAN_TX_CONTROL(0));
12213d0407baSopenharmony_ci        /* Disable OSP to get best performance */
12223d0407baSopenharmony_ci        mode &= ~DMA_CONTROL_OSP;
12233d0407baSopenharmony_ci        writel(mode, priv->ioaddr + DMA_CHAN_TX_CONTROL(0));
12243d0407baSopenharmony_ci    } else {
12253d0407baSopenharmony_ci        /* Disable OSF */
12263d0407baSopenharmony_ci        mode = readl(priv->ioaddr + DMA_CONTROL);
12273d0407baSopenharmony_ci        writel((mode & ~DMA_CONTROL_OSF), priv->ioaddr + DMA_CONTROL);
12283d0407baSopenharmony_ci    }
12293d0407baSopenharmony_ci
12303d0407baSopenharmony_ci    stmmac_enable_dma_irq(priv, priv->ioaddr, 0, 1, 1);
12313d0407baSopenharmony_ci
12323d0407baSopenharmony_ci    if (priv->hw->pcs) {
12333d0407baSopenharmony_ci        stmmac_pcs_ctrl_ane(priv, priv->hw, 1, priv->hw->ps, 0);
12343d0407baSopenharmony_ci    }
12353d0407baSopenharmony_ci
12363d0407baSopenharmony_ci    return 0;
12373d0407baSopenharmony_ciinit_error:
12383d0407baSopenharmony_ci    dwmac_rk_free_dma_desc_resources(priv, lb_priv);
12393d0407baSopenharmony_ci
12403d0407baSopenharmony_ci    return ret;
12413d0407baSopenharmony_ci}
12423d0407baSopenharmony_ci
12433d0407baSopenharmony_cistatic void dwmac_rk_release(struct net_device *dev, struct dwmac_rk_lb_priv *lb_priv)
12443d0407baSopenharmony_ci{
12453d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(dev);
12463d0407baSopenharmony_ci
12473d0407baSopenharmony_ci    stmmac_disable_dma_irq(priv, priv->ioaddr, 0, 0, 0);
12483d0407baSopenharmony_ci
12493d0407baSopenharmony_ci    /* Release and free the Rx/Tx resources */
12503d0407baSopenharmony_ci    dwmac_rk_free_dma_desc_resources(priv, lb_priv);
12513d0407baSopenharmony_ci}
12523d0407baSopenharmony_ci
12533d0407baSopenharmony_cistatic int dwmac_rk_get_max_delayline(struct stmmac_priv *priv)
12543d0407baSopenharmony_ci{
12553d0407baSopenharmony_ci    if (of_device_is_compatible(priv->device->of_node, "rockchip,rk3588-gmac")) {
12563d0407baSopenharmony_ci        return RK3588_MAX_DELAYLINE;
12573d0407baSopenharmony_ci    } else {
12583d0407baSopenharmony_ci        return MAX_DELAYLINE;
12593d0407baSopenharmony_ci    }
12603d0407baSopenharmony_ci}
12613d0407baSopenharmony_ci
12623d0407baSopenharmony_cistatic int dwmac_rk_phy_poll_reset(struct stmmac_priv *priv, int addr)
12633d0407baSopenharmony_ci{
12643d0407baSopenharmony_ci    /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
12653d0407baSopenharmony_ci    unsigned int val, retries = 12;
12663d0407baSopenharmony_ci    int ret;
12673d0407baSopenharmony_ci
12683d0407baSopenharmony_ci    val = mdiobus_read(priv->mii, addr, MII_BMCR);
12693d0407baSopenharmony_ci    mdiobus_write(priv->mii, addr, MII_BMCR, val | BMCR_RESET);
12703d0407baSopenharmony_ci
12713d0407baSopenharmony_ci    do {
12723d0407baSopenharmony_ci        msleep(0x32);
12733d0407baSopenharmony_ci        ret = mdiobus_read(priv->mii, addr, MII_BMCR);
12743d0407baSopenharmony_ci        if (ret < 0) {
12753d0407baSopenharmony_ci            return ret;
12763d0407baSopenharmony_ci        }
12773d0407baSopenharmony_ci    } while ((ret & BMCR_RESET) && --retries);
12783d0407baSopenharmony_ci    if (ret & BMCR_RESET) {
12793d0407baSopenharmony_ci        return -ETIMEDOUT;
12803d0407baSopenharmony_ci    }
12813d0407baSopenharmony_ci
12823d0407baSopenharmony_ci    msleep(1);
12833d0407baSopenharmony_ci    return 0;
12843d0407baSopenharmony_ci}
12853d0407baSopenharmony_ci
12863d0407baSopenharmony_cistatic int dwmac_rk_loopback_run(struct stmmac_priv *priv, struct dwmac_rk_lb_priv *lb_priv)
12873d0407baSopenharmony_ci{
12883d0407baSopenharmony_ci    struct net_device *ndev = priv->dev;
12893d0407baSopenharmony_ci    int phy_iface = dwmac_rk_get_phy_interface(priv);
12903d0407baSopenharmony_ci    int ndev_up, phy_addr;
12913d0407baSopenharmony_ci    int ret = -EINVAL;
12923d0407baSopenharmony_ci
12933d0407baSopenharmony_ci    if (!ndev || !priv->mii) {
12943d0407baSopenharmony_ci        return -EINVAL;
12953d0407baSopenharmony_ci    }
12963d0407baSopenharmony_ci
12973d0407baSopenharmony_ci    phy_addr = priv->dev->phydev->mdio.addr;
12983d0407baSopenharmony_ci    lb_priv->max_delay = dwmac_rk_get_max_delayline(priv);
12993d0407baSopenharmony_ci
13003d0407baSopenharmony_ci    rtnl_lock();
13013d0407baSopenharmony_ci    /* check the netdevice up or not */
13023d0407baSopenharmony_ci    ndev_up = ndev->flags & IFF_UP;
13033d0407baSopenharmony_ci
13043d0407baSopenharmony_ci    if (ndev_up) {
13053d0407baSopenharmony_ci        if (!netif_running(ndev) || !ndev->phydev) {
13063d0407baSopenharmony_ci            rtnl_unlock();
13073d0407baSopenharmony_ci            return -EINVAL;
13083d0407baSopenharmony_ci        }
13093d0407baSopenharmony_ci
13103d0407baSopenharmony_ci        /* check if the negotiation status */
13113d0407baSopenharmony_ci        if (ndev->phydev->state != PHY_NOLINK && ndev->phydev->state != PHY_RUNNING) {
13123d0407baSopenharmony_ci            rtnl_unlock();
13133d0407baSopenharmony_ci            pr_warn("Try again later, after negotiation done\n");
13143d0407baSopenharmony_ci            return -EAGAIN;
13153d0407baSopenharmony_ci        }
13163d0407baSopenharmony_ci
13173d0407baSopenharmony_ci        ndev->netdev_ops->ndo_stop(ndev);
13183d0407baSopenharmony_ci
13193d0407baSopenharmony_ci        if (priv->plat->stmmac_rst) {
13203d0407baSopenharmony_ci            reset_control_assert(priv->plat->stmmac_rst);
13213d0407baSopenharmony_ci        }
13223d0407baSopenharmony_ci        dwmac_rk_phy_poll_reset(priv, phy_addr);
13233d0407baSopenharmony_ci        if (priv->plat->stmmac_rst) {
13243d0407baSopenharmony_ci            reset_control_deassert(priv->plat->stmmac_rst);
13253d0407baSopenharmony_ci        }
13263d0407baSopenharmony_ci    }
13273d0407baSopenharmony_ci    /* wait for phy and controller ready */
13283d0407baSopenharmony_ci    usleep_range(0x186A0, 0x30D40);
13293d0407baSopenharmony_ci
13303d0407baSopenharmony_ci    dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed, true, phy_addr, true);
13313d0407baSopenharmony_ci
13323d0407baSopenharmony_ci    ret = dwmac_rk_init(ndev, lb_priv);
13333d0407baSopenharmony_ci    if (ret) {
13343d0407baSopenharmony_ci        goto exit_init;
13353d0407baSopenharmony_ci    }
13363d0407baSopenharmony_ci
13373d0407baSopenharmony_ci    dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed, true, phy_addr, false);
13383d0407baSopenharmony_ci
13393d0407baSopenharmony_ci    if (lb_priv->scan) {
13403d0407baSopenharmony_ci        /* scan only support for rgmii mode */
13413d0407baSopenharmony_ci        if (phy_iface != PHY_INTERFACE_MODE_RGMII && phy_iface != PHY_INTERFACE_MODE_RGMII_ID &&
13423d0407baSopenharmony_ci            phy_iface != PHY_INTERFACE_MODE_RGMII_RXID && phy_iface != PHY_INTERFACE_MODE_RGMII_TXID) {
13433d0407baSopenharmony_ci            ret = -EINVAL;
13443d0407baSopenharmony_ci            goto out;
13453d0407baSopenharmony_ci        }
13463d0407baSopenharmony_ci        ret = dwmac_rk_loopback_delayline_scan(priv, lb_priv);
13473d0407baSopenharmony_ci    } else {
13483d0407baSopenharmony_ci        lb_priv->id++;
13493d0407baSopenharmony_ci        lb_priv->tx = 0;
13503d0407baSopenharmony_ci        lb_priv->rx = 0;
13513d0407baSopenharmony_ci
13523d0407baSopenharmony_ci        lb_priv->packet = &dwmac_rk_tcp_attr;
13533d0407baSopenharmony_ci        ret = dwmac_rk_loopback_run_first(priv, lb_priv);
13543d0407baSopenharmony_ci    }
13553d0407baSopenharmony_ci
13563d0407baSopenharmony_ciout:
13573d0407baSopenharmony_ci    dwmac_rk_release(ndev, lb_priv);
13583d0407baSopenharmony_ci    dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed, false, phy_addr, false);
13593d0407baSopenharmony_ciexit_init:
13603d0407baSopenharmony_ci    if (ndev_up) {
13613d0407baSopenharmony_ci        ndev->netdev_ops->ndo_open(ndev);
13623d0407baSopenharmony_ci    }
13633d0407baSopenharmony_ci
13643d0407baSopenharmony_ci    rtnl_unlock();
13653d0407baSopenharmony_ci
13663d0407baSopenharmony_ci    return ret;
13673d0407baSopenharmony_ci}
13683d0407baSopenharmony_ci
13693d0407baSopenharmony_cistatic ssize_t rgmii_delayline_show(struct device *dev, struct device_attribute *attr, char *buf)
13703d0407baSopenharmony_ci{
13713d0407baSopenharmony_ci    struct net_device *ndev = dev_get_drvdata(dev);
13723d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(ndev);
13733d0407baSopenharmony_ci    int tx, rx;
13743d0407baSopenharmony_ci
13753d0407baSopenharmony_ci    dwmac_rk_get_rgmii_delayline(priv, &tx, &rx);
13763d0407baSopenharmony_ci
13773d0407baSopenharmony_ci    return sprintf(buf, "tx delayline: 0x%x, rx delayline: 0x%x\n", tx, rx);
13783d0407baSopenharmony_ci}
13793d0407baSopenharmony_ci
13803d0407baSopenharmony_cistatic ssize_t rgmii_delayline_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
13813d0407baSopenharmony_ci{
13823d0407baSopenharmony_ci    struct net_device *ndev = dev_get_drvdata(dev);
13833d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(ndev);
13843d0407baSopenharmony_ci    int tx = 0, rx = 0;
13853d0407baSopenharmony_ci    char tmp[32];
13863d0407baSopenharmony_ci    size_t buf_size = min(count, (sizeof(tmp) - 1));
13873d0407baSopenharmony_ci    char *data;
13883d0407baSopenharmony_ci
13893d0407baSopenharmony_ci    memset(tmp, 0, sizeof(tmp));
13903d0407baSopenharmony_ci    strncpy(tmp, buf, buf_size);
13913d0407baSopenharmony_ci
13923d0407baSopenharmony_ci    data = tmp;
13933d0407baSopenharmony_ci    data = strstr(data, " ");
13943d0407baSopenharmony_ci    if (!data) {
13953d0407baSopenharmony_ci        goto out;
13963d0407baSopenharmony_ci    }
13973d0407baSopenharmony_ci    *data = 0;
13983d0407baSopenharmony_ci    data++;
13993d0407baSopenharmony_ci
14003d0407baSopenharmony_ci    if (kstrtoint(tmp, 0, &tx) || tx > dwmac_rk_get_max_delayline(priv)) {
14013d0407baSopenharmony_ci        goto out;
14023d0407baSopenharmony_ci    }
14033d0407baSopenharmony_ci
14043d0407baSopenharmony_ci    if (kstrtoint(data, 0, &rx) || rx > dwmac_rk_get_max_delayline(priv)) {
14053d0407baSopenharmony_ci        goto out;
14063d0407baSopenharmony_ci    }
14073d0407baSopenharmony_ci
14083d0407baSopenharmony_ci    dwmac_rk_set_rgmii_delayline(priv, tx, rx);
14093d0407baSopenharmony_ci    pr_info("Set rgmii delayline tx: 0x%x, rx: 0x%x\n", tx, rx);
14103d0407baSopenharmony_ci
14113d0407baSopenharmony_ci    return count;
14123d0407baSopenharmony_ciout:
14133d0407baSopenharmony_ci    pr_err("wrong delayline value input, range is <0x0, 0x7f>\n");
14143d0407baSopenharmony_ci    pr_err("usage: <tx_delayline> <rx_delayline>\n");
14153d0407baSopenharmony_ci
14163d0407baSopenharmony_ci    return count;
14173d0407baSopenharmony_ci}
14183d0407baSopenharmony_cistatic DEVICE_ATTR_RW(rgmii_delayline);
14193d0407baSopenharmony_ci
14203d0407baSopenharmony_cistatic ssize_t mac_lb_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
14213d0407baSopenharmony_ci{
14223d0407baSopenharmony_ci    struct net_device *ndev = dev_get_drvdata(dev);
14233d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(ndev);
14243d0407baSopenharmony_ci    struct dwmac_rk_lb_priv *lb_priv;
14253d0407baSopenharmony_ci    int ret, speed;
14263d0407baSopenharmony_ci
14273d0407baSopenharmony_ci    lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL);
14283d0407baSopenharmony_ci    if (!lb_priv) {
14293d0407baSopenharmony_ci        return -ENOMEM;
14303d0407baSopenharmony_ci    }
14313d0407baSopenharmony_ci
14323d0407baSopenharmony_ci    ret = kstrtoint(buf, 0, &speed);
14333d0407baSopenharmony_ci    if (ret) {
14343d0407baSopenharmony_ci        kfree(lb_priv);
14353d0407baSopenharmony_ci        return count;
14363d0407baSopenharmony_ci    }
14373d0407baSopenharmony_ci    pr_info("MAC loopback speed set to %d\n", speed);
14383d0407baSopenharmony_ci
14393d0407baSopenharmony_ci    lb_priv->sysfs = 1;
14403d0407baSopenharmony_ci    lb_priv->type = LOOPBACK_TYPE_GMAC;
14413d0407baSopenharmony_ci    lb_priv->speed = speed;
14423d0407baSopenharmony_ci    lb_priv->scan = 0;
14433d0407baSopenharmony_ci
14443d0407baSopenharmony_ci    ret = dwmac_rk_loopback_run(priv, lb_priv);
14453d0407baSopenharmony_ci    kfree(lb_priv);
14463d0407baSopenharmony_ci
14473d0407baSopenharmony_ci    if (!ret) {
14483d0407baSopenharmony_ci        pr_info("MAC loopback: PASS\n");
14493d0407baSopenharmony_ci    } else {
14503d0407baSopenharmony_ci        pr_info("MAC loopback: FAIL\n");
14513d0407baSopenharmony_ci    }
14523d0407baSopenharmony_ci
14533d0407baSopenharmony_ci    return count;
14543d0407baSopenharmony_ci}
14553d0407baSopenharmony_cistatic DEVICE_ATTR_WO(mac_lb);
14563d0407baSopenharmony_ci
14573d0407baSopenharmony_cistatic ssize_t phy_lb_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
14583d0407baSopenharmony_ci{
14593d0407baSopenharmony_ci    struct net_device *ndev = dev_get_drvdata(dev);
14603d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(ndev);
14613d0407baSopenharmony_ci    struct dwmac_rk_lb_priv *lb_priv;
14623d0407baSopenharmony_ci    int ret, speed;
14633d0407baSopenharmony_ci
14643d0407baSopenharmony_ci    lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL);
14653d0407baSopenharmony_ci    if (!lb_priv) {
14663d0407baSopenharmony_ci        return -ENOMEM;
14673d0407baSopenharmony_ci    }
14683d0407baSopenharmony_ci
14693d0407baSopenharmony_ci    ret = kstrtoint(buf, 0, &speed);
14703d0407baSopenharmony_ci    if (ret) {
14713d0407baSopenharmony_ci        kfree(lb_priv);
14723d0407baSopenharmony_ci        return count;
14733d0407baSopenharmony_ci    }
14743d0407baSopenharmony_ci    pr_info("PHY loopback speed set to %d\n", speed);
14753d0407baSopenharmony_ci
14763d0407baSopenharmony_ci    lb_priv->sysfs = 1;
14773d0407baSopenharmony_ci    lb_priv->type = LOOPBACK_TYPE_PHY;
14783d0407baSopenharmony_ci    lb_priv->speed = speed;
14793d0407baSopenharmony_ci    lb_priv->scan = 0;
14803d0407baSopenharmony_ci
14813d0407baSopenharmony_ci    ret = dwmac_rk_loopback_run(priv, lb_priv);
14823d0407baSopenharmony_ci    if (!ret) {
14833d0407baSopenharmony_ci        pr_info("PHY loopback: PASS\n");
14843d0407baSopenharmony_ci    } else {
14853d0407baSopenharmony_ci        pr_info("PHY loopback: FAIL\n");
14863d0407baSopenharmony_ci    }
14873d0407baSopenharmony_ci
14883d0407baSopenharmony_ci    kfree(lb_priv);
14893d0407baSopenharmony_ci    return count;
14903d0407baSopenharmony_ci}
14913d0407baSopenharmony_cistatic DEVICE_ATTR_WO(phy_lb);
14923d0407baSopenharmony_ci
14933d0407baSopenharmony_cistatic ssize_t phy_lb_scan_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
14943d0407baSopenharmony_ci{
14953d0407baSopenharmony_ci    struct net_device *ndev = dev_get_drvdata(dev);
14963d0407baSopenharmony_ci    struct stmmac_priv *priv = netdev_priv(ndev);
14973d0407baSopenharmony_ci    struct dwmac_rk_lb_priv *lb_priv;
14983d0407baSopenharmony_ci    int ret, speed;
14993d0407baSopenharmony_ci
15003d0407baSopenharmony_ci    lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL);
15013d0407baSopenharmony_ci    if (!lb_priv) {
15023d0407baSopenharmony_ci        return -ENOMEM;
15033d0407baSopenharmony_ci    }
15043d0407baSopenharmony_ci
15053d0407baSopenharmony_ci    ret = kstrtoint(buf, 0, &speed);
15063d0407baSopenharmony_ci    if (ret) {
15073d0407baSopenharmony_ci        kfree(lb_priv);
15083d0407baSopenharmony_ci        return count;
15093d0407baSopenharmony_ci    }
15103d0407baSopenharmony_ci    pr_info("Delayline scan speed set to %d\n", speed);
15113d0407baSopenharmony_ci
15123d0407baSopenharmony_ci    lb_priv->sysfs = 1;
15133d0407baSopenharmony_ci    lb_priv->type = LOOPBACK_TYPE_PHY;
15143d0407baSopenharmony_ci    lb_priv->speed = speed;
15153d0407baSopenharmony_ci    lb_priv->scan = 1;
15163d0407baSopenharmony_ci
15173d0407baSopenharmony_ci    dwmac_rk_loopback_run(priv, lb_priv);
15183d0407baSopenharmony_ci
15193d0407baSopenharmony_ci    kfree(lb_priv);
15203d0407baSopenharmony_ci    return count;
15213d0407baSopenharmony_ci}
15223d0407baSopenharmony_cistatic DEVICE_ATTR_WO(phy_lb_scan);
15233d0407baSopenharmony_ci
15243d0407baSopenharmony_ciint dwmac_rk_create_loopback_sysfs(struct device *device)
15253d0407baSopenharmony_ci{
15263d0407baSopenharmony_ci    int ret;
15273d0407baSopenharmony_ci
15283d0407baSopenharmony_ci    ret = device_create_file(device, &dev_attr_rgmii_delayline);
15293d0407baSopenharmony_ci    if (ret) {
15303d0407baSopenharmony_ci        return ret;
15313d0407baSopenharmony_ci    }
15323d0407baSopenharmony_ci
15333d0407baSopenharmony_ci    ret = device_create_file(device, &dev_attr_mac_lb);
15343d0407baSopenharmony_ci    if (ret) {
15353d0407baSopenharmony_ci        goto remove_rgmii_delayline;
15363d0407baSopenharmony_ci    }
15373d0407baSopenharmony_ci
15383d0407baSopenharmony_ci    ret = device_create_file(device, &dev_attr_phy_lb);
15393d0407baSopenharmony_ci    if (ret) {
15403d0407baSopenharmony_ci        goto remove_mac_lb;
15413d0407baSopenharmony_ci    }
15423d0407baSopenharmony_ci
15433d0407baSopenharmony_ci    ret = device_create_file(device, &dev_attr_phy_lb_scan);
15443d0407baSopenharmony_ci    if (ret) {
15453d0407baSopenharmony_ci        goto remove_phy_lb;
15463d0407baSopenharmony_ci    }
15473d0407baSopenharmony_ci
15483d0407baSopenharmony_ci    return 0;
15493d0407baSopenharmony_ci
15503d0407baSopenharmony_ciremove_rgmii_delayline:
15513d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_rgmii_delayline);
15523d0407baSopenharmony_ci
15533d0407baSopenharmony_ciremove_mac_lb:
15543d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_mac_lb);
15553d0407baSopenharmony_ci
15563d0407baSopenharmony_ciremove_phy_lb:
15573d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_phy_lb);
15583d0407baSopenharmony_ci
15593d0407baSopenharmony_ci    return ret;
15603d0407baSopenharmony_ci}
15613d0407baSopenharmony_ci
15623d0407baSopenharmony_ciint dwmac_rk_remove_loopback_sysfs(struct device *device)
15633d0407baSopenharmony_ci{
15643d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_rgmii_delayline);
15653d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_mac_lb);
15663d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_phy_lb);
15673d0407baSopenharmony_ci    device_remove_file(device, &dev_attr_phy_lb_scan);
15683d0407baSopenharmony_ci
15693d0407baSopenharmony_ci    return 0;
15703d0407baSopenharmony_ci}
1571