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