net: ethernet: stmmac: Fix loopback at dwmac-ethtool for rk3588

slove following issues:
 - socket buffer is over;
 - store phyaddr;
 - get max delayline value for rk3588;
 - split mac setting and phy setting for loopback to fix flow;
 - add rgmii-rxid interface loopback;
 - enable mtl configuration for multi queue;
 - use phy softreset instead of hard reset.

Signed-off-by: David Wu <david.wu@rock-chips.com>
Change-Id: I8da5a42948b34bf4a256a6f425c0ed98590ddea6
This commit is contained in:
David Wu
2021-11-11 12:52:43 +08:00
committed by Tao Huang
parent 2db29d7f51
commit 3e76ff28cd

View File

@@ -9,6 +9,7 @@
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
#include <linux/regmap.h>
@@ -85,6 +86,7 @@ struct dwmac_rk_lb_priv {
int rx;
int final_tx;
int final_rx;
int max_delay;
};
#define DMA_CONTROL_OSP BIT(4)
@@ -99,6 +101,7 @@ struct dwmac_rk_lb_priv {
#define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES)
#define MAX_DELAYLINE 0x7f
#define RK3588_MAX_DELAYLINE 0xc7
#define SCAN_STEP 0x5
#define SCAN_VALID_RANGE 0xA
@@ -119,7 +122,8 @@ static __maybe_unused struct dwmac_rk_packet_attrs dwmac_rk_tcp_attr = {
.size = 1024,
};
static int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed)
static int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed,
int addr, bool phy)
{
u32 ctrl;
int phy_val;
@@ -128,25 +132,30 @@ static int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed)
ctrl &= ~priv->hw->link.speed_mask;
ctrl |= GMAC_CONTROL_LM;
phy_val = mdiobus_read(priv->mii, priv->plat->phy_addr, MII_BMCR);
if (phy)
phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
switch (speed) {
case LOOPBACK_SPEED1000:
ctrl |= priv->hw->link.speed1000;
phy_val |= BMCR_ANENABLE;
phy_val |= BMCR_SPEED1000;
if (phy) {
phy_val &= ~BMCR_SPEED100;
phy_val |= BMCR_SPEED1000;
}
break;
case LOOPBACK_SPEED100:
ctrl |= priv->hw->link.speed100;
phy_val &= ~BMCR_ANENABLE;
phy_val &= ~BMCR_SPEED1000;
phy_val |= BMCR_SPEED100;
if (phy) {
phy_val &= ~BMCR_SPEED1000;
phy_val |= BMCR_SPEED100;
}
break;
case LOOPBACK_SPEED10:
ctrl |= priv->hw->link.speed10;
phy_val &= ~BMCR_ANENABLE;
phy_val &= ~BMCR_SPEED1000;
phy_val &= ~BMCR_SPEED100;
if (phy) {
phy_val &= ~BMCR_SPEED1000;
phy_val &= ~BMCR_SPEED100;
}
break;
default:
return -EPERM;
@@ -155,9 +164,14 @@ static int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed)
ctrl |= priv->hw->link.duplex;
writel(ctrl, priv->ioaddr + GMAC_CONTROL);
phy_val |= BMCR_FULLDPLX;
mdiobus_write(priv->mii, priv->plat->phy_addr, MII_BMCR, phy_val);
phy_val = mdiobus_read(priv->mii, priv->plat->phy_addr, MII_BMCR);
if (phy) {
phy_val &= ~BMCR_PDOWN;
phy_val &= ~BMCR_ANENABLE;
phy_val &= ~BMCR_PDOWN;
phy_val |= BMCR_FULLDPLX;
mdiobus_write(priv->mii, addr, MII_BMCR, phy_val);
phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
}
if (likely(priv->plat->fix_mac_speed))
priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed);
@@ -165,7 +179,7 @@ static int dwmac_rk_enable_mac_loopback(struct stmmac_priv *priv, int speed)
return 0;
}
static int dwmac_rk_disable_mac_loopback(struct stmmac_priv *priv)
static int dwmac_rk_disable_mac_loopback(struct stmmac_priv *priv, int addr)
{
u32 ctrl;
int phy_val;
@@ -174,25 +188,27 @@ static int dwmac_rk_disable_mac_loopback(struct stmmac_priv *priv)
ctrl &= ~GMAC_CONTROL_LM;
writel(ctrl, priv->ioaddr + GMAC_CONTROL);
phy_val = mdiobus_read(priv->mii, priv->plat->phy_addr, MII_BMCR);
phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
phy_val |= BMCR_ANENABLE;
mdiobus_write(priv->mii, priv->plat->phy_addr, MII_BMCR, phy_val);
phy_val = mdiobus_read(priv->mii, priv->plat->phy_addr, MII_BMCR);
mdiobus_write(priv->mii, addr, MII_BMCR, phy_val);
phy_val = mdiobus_read(priv->mii, addr, MII_BMCR);
return 0;
}
static int dwmac_rk_set_mac_loopback(struct stmmac_priv *priv,
int speed, bool enable)
int speed, bool enable,
int addr, bool phy)
{
if (enable)
return dwmac_rk_enable_mac_loopback(priv, speed);
return dwmac_rk_enable_mac_loopback(priv, speed, addr, phy);
else
return dwmac_rk_disable_mac_loopback(priv);
return dwmac_rk_disable_mac_loopback(priv, addr);
}
static int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed)
static int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed,
int addr, bool phy)
{
u32 ctrl;
int val;
@@ -200,25 +216,30 @@ static int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed)
ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
ctrl &= ~priv->hw->link.speed_mask;
val = mdiobus_read(priv->mii, 0, MII_BMCR);
val &= ~BMCR_ANENABLE;
val |= BMCR_LOOPBACK;
if (phy)
val = mdiobus_read(priv->mii, addr, MII_BMCR);
switch (speed) {
case LOOPBACK_SPEED1000:
ctrl |= priv->hw->link.speed1000;
val |= BMCR_SPEED1000;
if (phy) {
val &= ~BMCR_SPEED100;
val |= BMCR_SPEED1000;
}
break;
case LOOPBACK_SPEED100:
ctrl |= priv->hw->link.speed100;
val &= ~BMCR_SPEED1000;
val |= BMCR_SPEED100;
if (phy) {
val &= ~BMCR_SPEED1000;
val |= BMCR_SPEED100;
}
break;
case LOOPBACK_SPEED10:
ctrl |= priv->hw->link.speed10;
val &= ~BMCR_SPEED1000;
val &= ~BMCR_SPEED100;
if (phy) {
val &= ~BMCR_SPEED1000;
val &= ~BMCR_SPEED100;
}
break;
default:
return -EPERM;
@@ -227,9 +248,14 @@ static int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed)
ctrl |= priv->hw->link.duplex;
writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
val |= BMCR_FULLDPLX;
mdiobus_write(priv->mii, 0, MII_BMCR, val);
val = mdiobus_read(priv->mii, 0, MII_BMCR);
if (phy) {
val |= BMCR_FULLDPLX;
val &= ~BMCR_PDOWN;
val &= ~BMCR_ANENABLE;
val |= BMCR_LOOPBACK;
mdiobus_write(priv->mii, addr, MII_BMCR, val);
val = mdiobus_read(priv->mii, addr, MII_BMCR);
}
if (likely(priv->plat->fix_mac_speed))
priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed);
@@ -237,40 +263,43 @@ static int dwmac_rk_enable_phy_loopback(struct stmmac_priv *priv, int speed)
return 0;
}
static int dwmac_rk_disable_phy_loopback(struct stmmac_priv *priv)
static int dwmac_rk_disable_phy_loopback(struct stmmac_priv *priv, int addr)
{
int val;
val = mdiobus_read(priv->mii, 0, MII_BMCR);
val = mdiobus_read(priv->mii, addr, MII_BMCR);
val |= BMCR_ANENABLE;
val &= ~BMCR_LOOPBACK;
mdiobus_write(priv->mii, 0, MII_BMCR, val);
val = mdiobus_read(priv->mii, priv->plat->phy_addr, MII_BMCR);
mdiobus_write(priv->mii, addr, MII_BMCR, val);
val = mdiobus_read(priv->mii, addr, MII_BMCR);
return 0;
}
static int dwmac_rk_set_phy_loopback(struct stmmac_priv *priv,
int speed, bool enable)
int speed, bool enable,
int addr, bool phy)
{
if (enable)
return dwmac_rk_enable_phy_loopback(priv, speed);
return dwmac_rk_enable_phy_loopback(priv, speed,
addr, phy);
else
return dwmac_rk_disable_phy_loopback(priv);
return dwmac_rk_disable_phy_loopback(priv, addr);
}
static int dwmac_rk_set_loopback(struct stmmac_priv *priv,
int type, int speed, bool enable)
int type, int speed, bool enable,
int addr, bool phy)
{
int ret;
switch (type) {
case LOOPBACK_TYPE_PHY:
ret = dwmac_rk_set_phy_loopback(priv, speed, enable);
ret = dwmac_rk_set_phy_loopback(priv, speed, enable, addr, phy);
break;
case LOOPBACK_TYPE_GMAC:
ret = dwmac_rk_set_mac_loopback(priv, speed, enable);
ret = dwmac_rk_set_mac_loopback(priv, speed, enable, addr, phy);
break;
default:
ret = -EOPNOTSUPP;
@@ -565,6 +594,7 @@ static int dwmac_rk_rx_validate(struct stmmac_priv *priv,
struct sk_buff *skb;
int coe = priv->hw->rx_csum;
unsigned int frame_len;
int ret;
p = lb_priv->dma_rx;
skb = lb_priv->rx_skbuff;
@@ -584,7 +614,11 @@ static int dwmac_rk_rx_validate(struct stmmac_priv *priv,
frame_len -= ETH_FCS_LEN;
skb_put(skb, frame_len);
return dwmac_rk_loopback_validate(priv, lb_priv, skb);
ret = dwmac_rk_loopback_validate(priv, lb_priv, skb);
dwmac_rk_rx_clean(priv, lb_priv);
dwmac_rk_rx_fill(priv, lb_priv);
return ret;
}
static int dwmac_rk_get_desc_status(struct stmmac_priv *priv,
@@ -771,9 +805,20 @@ static int dwmac_rk_loopback_with_identify(struct stmmac_priv *priv,
return __dwmac_rk_loopback_run(priv, lb_priv);
}
static inline bool dwmac_rk_delayline_is_valid(int tx, int rx)
static inline bool dwmac_rk_delayline_is_txvalid(struct dwmac_rk_lb_priv *lb_priv,
int tx)
{
if ((tx > 0 && tx < MAX_DELAYLINE) && (rx > 0 && rx < MAX_DELAYLINE))
if (tx > 0 && tx < lb_priv->max_delay)
return true;
else
return false;
}
static inline bool dwmac_rk_delayline_is_valid(struct dwmac_rk_lb_priv *lb_priv,
int tx, int rx)
{
if ((tx > 0 && tx < lb_priv->max_delay) &&
(rx > 0 && rx < lb_priv->max_delay))
return true;
else
return false;
@@ -784,7 +829,7 @@ static int dwmac_rk_delayline_scan_cross(struct stmmac_priv *priv,
{
int tx_left, tx_right, rx_up, rx_down;
int i, j, tx_index, rx_index;
int tx_mid, rx_mid;
int tx_mid = 0, rx_mid = 0;
/* initiation */
tx_index = SCAN_STEP;
@@ -792,12 +837,12 @@ static int dwmac_rk_delayline_scan_cross(struct stmmac_priv *priv,
re_scan:
/* start from rx based on the experience */
for (i = rx_index; i <= (MAX_DELAYLINE - SCAN_STEP); i += SCAN_STEP) {
for (i = rx_index; i <= (lb_priv->max_delay - SCAN_STEP); i += SCAN_STEP) {
tx_left = 0;
tx_right = 0;
tx_mid = 0;
for (j = tx_index; j <= (MAX_DELAYLINE - SCAN_STEP);
for (j = tx_index; j <= (lb_priv->max_delay - SCAN_STEP);
j += SCAN_STEP) {
if (!dwmac_rk_loopback_with_identify(priv,
lb_priv, j, i)) {
@@ -815,14 +860,14 @@ re_scan:
}
/* Worst case: reach the end */
if (i >= (MAX_DELAYLINE - SCAN_STEP))
if (i >= (lb_priv->max_delay - SCAN_STEP))
goto end;
rx_up = 0;
rx_down = 0;
/* look for rx_mid base on the tx_mid */
for (i = SCAN_STEP; i <= (MAX_DELAYLINE - SCAN_STEP);
for (i = SCAN_STEP; i <= (lb_priv->max_delay - SCAN_STEP);
i += SCAN_STEP) {
if (!dwmac_rk_loopback_with_identify(priv, lb_priv,
tx_mid, i)) {
@@ -841,23 +886,24 @@ re_scan:
goto re_scan;
}
if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) {
if (dwmac_rk_delayline_is_valid(lb_priv, tx_mid, rx_mid)) {
lb_priv->final_tx = tx_mid;
lb_priv->final_rx = rx_mid;
pr_info("Find suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n",
pr_info("Find available tx_delay = 0x%02x, rx_delay = 0x%02x\n",
lb_priv->final_tx, lb_priv->final_rx);
return 0;
}
end:
pr_err("Can't find suitable delayline\n");
pr_err("Can't find available delayline\n");
return -ENXIO;
}
static int dwmac_rk_delayline_scan(struct stmmac_priv *priv,
struct dwmac_rk_lb_priv *lb_priv)
{
int phy_iface = dwmac_rk_get_phy_interface(priv);
int tx, rx, tx_sum, rx_sum, count;
int tx_mid, rx_mid;
int ret = -ENXIO;
@@ -866,9 +912,11 @@ static int dwmac_rk_delayline_scan(struct stmmac_priv *priv,
rx_sum = 0;
count = 0;
for (rx = 0x0; rx <= MAX_DELAYLINE; rx++) {
printk(KERN_CONT "RX(0x%02x):", rx);
for (tx = 0x0; tx <= MAX_DELAYLINE; tx++) {
for (rx = 0x0; rx <= lb_priv->max_delay; rx++) {
if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID)
rx = -1;
printk(KERN_CONT "RX(%03d):", rx);
for (tx = 0x0; tx <= lb_priv->max_delay; tx++) {
if (!dwmac_rk_loopback_with_identify(priv,
lb_priv, tx, rx)) {
tx_sum += tx;
@@ -880,24 +928,40 @@ static int dwmac_rk_delayline_scan(struct stmmac_priv *priv,
}
}
printk(KERN_CONT "\n");
if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID)
break;
}
if (tx_sum && rx_sum && count) {
tx_mid = tx_sum / count;
rx_mid = rx_sum / count;
if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) {
lb_priv->final_tx = tx_mid;
lb_priv->final_rx = rx_mid;
ret = 0;
if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID) {
if (dwmac_rk_delayline_is_txvalid(lb_priv, tx_mid)) {
lb_priv->final_tx = tx_mid;
lb_priv->final_rx = -1;
ret = 0;
}
} else {
if (dwmac_rk_delayline_is_valid(lb_priv, tx_mid, rx_mid)) {
lb_priv->final_tx = tx_mid;
lb_priv->final_rx = rx_mid;
ret = 0;
}
}
}
if (ret)
if (ret) {
pr_err("\nCan't find suitable delayline\n");
else
pr_info("\nFind suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n",
lb_priv->final_tx, lb_priv->final_rx);
} else {
if (phy_iface == PHY_INTERFACE_MODE_RGMII_RXID)
pr_info("Find available tx_delay = 0x%02x, rx_delay = disable\n",
lb_priv->final_tx);
else
pr_info("\nFind suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n",
lb_priv->final_tx, lb_priv->final_rx);
}
return ret;
}
@@ -1090,6 +1154,39 @@ static void dwmac_rk_dma_operation_mode(struct stmmac_priv *priv,
}
}
static void dwmac_rk_rx_queue_dma_chan_map(struct stmmac_priv *priv)
{
u32 rx_queues_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
u32 queue;
u32 chan;
for (queue = 0; queue < rx_queues_count; queue++) {
chan = priv->plat->rx_queues_cfg[queue].chan;
stmmac_map_mtl_to_dma(priv, priv->hw, queue, chan);
}
}
static void dwmac_rk_mac_enable_rx_queues(struct stmmac_priv *priv)
{
u32 rx_queues_count = min_t(u32, priv->plat->rx_queues_to_use, 1);
int queue;
u8 mode;
for (queue = 0; queue < rx_queues_count; queue++) {
mode = priv->plat->rx_queues_cfg[queue].mode_to_use;
stmmac_rx_queue_enable(priv, priv->hw, mode, queue);
}
}
static void dwmac_rk_mtl_configuration(struct stmmac_priv *priv)
{
/* Map RX MTL to DMA channels */
dwmac_rk_rx_queue_dma_chan_map(priv);
/* Enable MAC RX Queues */
dwmac_rk_mac_enable_rx_queues(priv);
}
static int dwmac_rk_init(struct net_device *dev,
struct dwmac_rk_lb_priv *lb_priv)
{
@@ -1129,6 +1226,8 @@ static int dwmac_rk_init(struct net_device *dev,
/* Initialize the MAC Core */
stmmac_core_init(priv, priv->hw, dev);
dwmac_rk_mtl_configuration(priv);
ret = priv->hw->mac->rx_ipc(priv->hw);
if (!ret) {
pr_warn(" RX IPC Checksum Offload disabled\n");
@@ -1173,17 +1272,51 @@ static void dwmac_rk_release(struct net_device *dev,
dwmac_rk_free_dma_desc_resources(priv, lb_priv);
}
static int dwmac_rk_get_max_delayline(struct stmmac_priv *priv)
{
if (of_device_is_compatible(priv->device->of_node,
"rockchip,rk3588-gmac"))
return RK3588_MAX_DELAYLINE;
else
return MAX_DELAYLINE;
}
static int dwmac_rk_phy_poll_reset(struct stmmac_priv *priv, int addr)
{
/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
unsigned int val, retries = 12;
int ret;
val = mdiobus_read(priv->mii, addr, MII_BMCR);
mdiobus_write(priv->mii, addr, MII_BMCR, val | BMCR_RESET);
do {
msleep(50);
ret = mdiobus_read(priv->mii, addr, MII_BMCR);
if (ret < 0)
return ret;
} while (ret & BMCR_RESET && --retries);
if (ret & BMCR_RESET)
return -ETIMEDOUT;
msleep(1);
return 0;
}
static int dwmac_rk_loopback_run(struct stmmac_priv *priv,
struct dwmac_rk_lb_priv *lb_priv)
{
struct net_device *ndev = priv->dev;
int phy_iface = dwmac_rk_get_phy_interface(priv);
int ndev_up;
int ndev_up, phy_addr;
int ret = -EINVAL;
if (!ndev || !priv->mii)
return -EINVAL;
phy_addr = priv->dev->phydev->mdio.addr;
lb_priv->max_delay = dwmac_rk_get_max_delayline(priv);
rtnl_lock();
/* check the netdevice up or not */
ndev_up = ndev->flags & IFF_UP;
@@ -1206,25 +1339,29 @@ static int dwmac_rk_loopback_run(struct stmmac_priv *priv,
if (priv->plat->stmmac_rst)
reset_control_assert(priv->plat->stmmac_rst);
if (priv->mii)
priv->mii->reset(priv->mii);
dwmac_rk_phy_poll_reset(priv, phy_addr);
if (priv->plat->stmmac_rst)
reset_control_deassert(priv->plat->stmmac_rst);
}
/* wait for phy and controller ready */
usleep_range(100000, 200000);
dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed,
true, phy_addr, true);
ret = dwmac_rk_init(ndev, lb_priv);
if (ret)
goto exit_init;
dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed, true);
dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed,
true, phy_addr, false);
if (lb_priv->scan) {
/* scan only support for rgmii mode */
if (phy_iface != PHY_INTERFACE_MODE_RGMII &&
phy_iface != PHY_INTERFACE_MODE_RGMII_ID) {
phy_iface != PHY_INTERFACE_MODE_RGMII_ID &&
phy_iface != PHY_INTERFACE_MODE_RGMII_RXID &&
phy_iface != PHY_INTERFACE_MODE_RGMII_TXID) {
ret = -EINVAL;
goto out;
}
@@ -1240,8 +1377,8 @@ static int dwmac_rk_loopback_run(struct stmmac_priv *priv,
out:
dwmac_rk_release(ndev, lb_priv);
dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed, false);
dwmac_rk_set_loopback(priv, lb_priv->type, lb_priv->speed,
false, phy_addr, false);
exit_init:
if (ndev_up)
ndev->netdev_ops->ndo_open(ndev);
@@ -1286,10 +1423,10 @@ static ssize_t rgmii_delayline_store(struct device *dev,
*data = 0;
data++;
if (kstrtoint(tmp, 0, &tx) || tx > MAX_DELAYLINE)
if (kstrtoint(tmp, 0, &tx) || tx > dwmac_rk_get_max_delayline(priv))
goto out;
if (kstrtoint(data, 0, &rx) || rx > MAX_DELAYLINE)
if (kstrtoint(data, 0, &rx) || rx > dwmac_rk_get_max_delayline(priv))
goto out;
dwmac_rk_set_rgmii_delayline(priv, tx, rx);