summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/stmicro/stmmac
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac')
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h22
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c213
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c45
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.c298
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.h52
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c39
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c168
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c34
13 files changed, 734 insertions, 165 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index ff3f83b86d10..972e4ef6d414 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -4,7 +4,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o dwmac4_descs.o \
- dwmac4_dma.o dwmac4_lib.o dwmac4_core.o $(stmmac-y)
+ dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o $(stmmac-y)
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 2ffe76c0ff74..ad2388aee463 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -38,6 +38,8 @@
#define DWMAC_CORE_3_40 0x34
#define DWMAC_CORE_3_50 0x35
#define DWMAC_CORE_4_00 0x40
+#define DWMAC_CORE_5_00 0x50
+#define DWMAC_CORE_5_10 0x51
#define STMMAC_CHAN0 0 /* Always supported and default for all chips */
/* These need to be power of two, and >= 4 */
@@ -174,6 +176,17 @@ struct stmmac_extra_stats {
unsigned long tx_tso_nfrags;
};
+/* Safety Feature statistics exposed by ethtool */
+struct stmmac_safety_stats {
+ unsigned long mac_errors[32];
+ unsigned long mtl_errors[32];
+ unsigned long dma_errors[32];
+};
+
+/* Number of fields in Safety Stats */
+#define STMMAC_SAFETY_FEAT_SIZE \
+ (sizeof(struct stmmac_safety_stats) / sizeof(unsigned long))
+
/* CSR Frequency Access Defines*/
#define CSR_F_35M 35000000
#define CSR_F_60M 60000000
@@ -336,6 +349,8 @@ struct dma_features {
/* TX and RX FIFO sizes */
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
+ /* Automotive Safety Package */
+ unsigned int asp;
};
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
@@ -532,6 +547,13 @@ struct stmmac_ops {
bool loopback);
void (*pcs_rane)(void __iomem *ioaddr, bool restart);
void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv *adv);
+ /* Safety Features */
+ int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp);
+ bool (*safety_feat_irq_status)(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats);
+ const char *(*safety_feat_dump)(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count);
};
/* PTP and HW Timer helpers */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
index 5270d26f0bc6..7cb794094a70 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -1,5 +1,5 @@
/*
- * Amlogic Meson8b and GXBB DWMAC glue layer
+ * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
*
* Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
@@ -48,26 +48,18 @@
#define MUX_CLK_NUM_PARENTS 2
struct meson8b_dwmac {
- struct platform_device *pdev;
-
+ struct device *dev;
void __iomem *regs;
-
phy_interface_t phy_mode;
+ struct clk *rgmii_tx_clk;
+ u32 tx_delay_ns;
+};
+struct meson8b_dwmac_clk_configs {
struct clk_mux m250_mux;
- struct clk *m250_mux_clk;
- struct clk *m250_mux_parent[MUX_CLK_NUM_PARENTS];
-
struct clk_divider m250_div;
- struct clk *m250_div_clk;
-
struct clk_fixed_factor fixed_div2;
- struct clk *fixed_div2_clk;
-
struct clk_gate rgmii_tx_en;
- struct clk *rgmii_tx_en_clk;
-
- u32 tx_delay_ns;
};
static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
@@ -82,106 +74,99 @@ static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
writel(data, dwmac->regs + reg);
}
-static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
+static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac,
+ const char *name_suffix,
+ const char **parent_names,
+ int num_parents,
+ const struct clk_ops *ops,
+ struct clk_hw *hw)
{
struct clk_init_data init;
- int i, ret;
- struct device *dev = &dwmac->pdev->dev;
char clk_name[32];
- const char *clk_div_parents[1];
- const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
+
+ snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dwmac->dev),
+ name_suffix);
+
+ init.name = clk_name;
+ init.ops = ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ hw->init = &init;
+
+ return devm_clk_register(dwmac->dev, hw);
+}
+
+static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
+{
+ int i, ret;
+ struct clk *clk;
+ struct device *dev = dwmac->dev;
+ const char *parent_name, *mux_parent_names[MUX_CLK_NUM_PARENTS];
+ struct meson8b_dwmac_clk_configs *clk_configs;
+
+ clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
+ if (!clk_configs)
+ return -ENOMEM;
/* get the mux parents from DT */
for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
char name[16];
snprintf(name, sizeof(name), "clkin%d", i);
- dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
- if (IS_ERR(dwmac->m250_mux_parent[i])) {
- ret = PTR_ERR(dwmac->m250_mux_parent[i]);
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Missing clock %s\n", name);
return ret;
}
- mux_parent_names[i] =
- __clk_get_name(dwmac->m250_mux_parent[i]);
+ mux_parent_names[i] = __clk_get_name(clk);
}
- /* create the m250_mux */
- snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
- init.name = clk_name;
- init.ops = &clk_mux_ops;
- init.flags = CLK_SET_RATE_PARENT;
- init.parent_names = mux_parent_names;
- init.num_parents = MUX_CLK_NUM_PARENTS;
-
- dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
- dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
- dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
- dwmac->m250_mux.flags = 0;
- dwmac->m250_mux.table = NULL;
- dwmac->m250_mux.hw.init = &init;
-
- dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
- if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
- return PTR_ERR(dwmac->m250_mux_clk);
-
- /* create the m250_div */
- snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
- init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
- init.ops = &clk_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
- dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
- dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
- dwmac->m250_div.hw.init = &init;
- dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED |
+ clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
+ clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
+ clk = meson8b_dwmac_register_clk(dwmac, "m250_sel", mux_parent_names,
+ MUX_CLK_NUM_PARENTS, &clk_mux_ops,
+ &clk_configs->m250_mux.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
+ clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
+ clk_configs->m250_div.flags = CLK_DIVIDER_ONE_BASED |
CLK_DIVIDER_ALLOW_ZERO |
CLK_DIVIDER_ROUND_CLOSEST;
-
- dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
- if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
- return PTR_ERR(dwmac->m250_div_clk);
-
- /* create the fixed_div2 */
- snprintf(clk_name, sizeof(clk_name), "%s#fixed_div2", dev_name(dev));
- init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
- init.ops = &clk_fixed_factor_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->fixed_div2.mult = 1;
- dwmac->fixed_div2.div = 2;
- dwmac->fixed_div2.hw.init = &init;
-
- dwmac->fixed_div2_clk = devm_clk_register(dev, &dwmac->fixed_div2.hw);
- if (WARN_ON(IS_ERR(dwmac->fixed_div2_clk)))
- return PTR_ERR(dwmac->fixed_div2_clk);
-
- /* create the rgmii_tx_en */
- init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#rgmii_tx_en",
- dev_name(dev));
- init.ops = &clk_gate_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_div_parents[0] = __clk_get_name(dwmac->fixed_div2_clk);
- init.parent_names = clk_div_parents;
- init.num_parents = ARRAY_SIZE(clk_div_parents);
-
- dwmac->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
- dwmac->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
- dwmac->rgmii_tx_en.hw.init = &init;
-
- dwmac->rgmii_tx_en_clk = devm_clk_register(dev,
- &dwmac->rgmii_tx_en.hw);
- if (WARN_ON(IS_ERR(dwmac->rgmii_tx_en_clk)))
- return PTR_ERR(dwmac->rgmii_tx_en_clk);
+ clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_name, 1,
+ &clk_divider_ops,
+ &clk_configs->m250_div.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->fixed_div2.mult = 1;
+ clk_configs->fixed_div2.div = 2;
+ clk = meson8b_dwmac_register_clk(dwmac, "fixed_div2", &parent_name, 1,
+ &clk_fixed_factor_ops,
+ &clk_configs->fixed_div2.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ parent_name = __clk_get_name(clk);
+ clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
+ clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
+ clk = meson8b_dwmac_register_clk(dwmac, "rgmii_tx_en", &parent_name, 1,
+ &clk_gate_ops,
+ &clk_configs->rgmii_tx_en.hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ dwmac->rgmii_tx_clk = clk;
return 0;
}
@@ -219,19 +204,23 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
* a register) based on the line-speed (125MHz for Gbit speeds,
* 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
*/
- ret = clk_set_rate(dwmac->rgmii_tx_en_clk, 125 * 1000 * 1000);
+ ret = clk_set_rate(dwmac->rgmii_tx_clk, 125 * 1000 * 1000);
if (ret) {
- dev_err(&dwmac->pdev->dev,
+ dev_err(dwmac->dev,
"failed to set RGMII TX clock\n");
return ret;
}
- ret = clk_prepare_enable(dwmac->rgmii_tx_en_clk);
+ ret = clk_prepare_enable(dwmac->rgmii_tx_clk);
if (ret) {
- dev_err(&dwmac->pdev->dev,
+ dev_err(dwmac->dev,
"failed to enable the RGMII TX clock\n");
return ret;
}
+
+ devm_add_action_or_reset(dwmac->dev,
+ (void(*)(void *))clk_disable_unprepare,
+ dwmac->rgmii_tx_clk);
break;
case PHY_INTERFACE_MODE_RMII:
@@ -251,7 +240,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
break;
default:
- dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
+ dev_err(dwmac->dev, "unsupported phy-mode %s\n",
phy_modes(dwmac->phy_mode));
return -EINVAL;
}
@@ -292,7 +281,7 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
goto err_remove_config_dt;
}
- dwmac->pdev = pdev;
+ dwmac->dev = &pdev->dev;
dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
if (dwmac->phy_mode < 0) {
dev_err(&pdev->dev, "missing phy-mode property\n");
@@ -317,31 +306,19 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
- goto err_clk_disable;
+ goto err_remove_config_dt;
return 0;
-err_clk_disable:
- if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
- clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
err_remove_config_dt:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
}
-static int meson8b_dwmac_remove(struct platform_device *pdev)
-{
- struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
-
- if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
- clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
-
- return stmmac_pltfr_remove(pdev);
-}
-
static const struct of_device_id meson8b_dwmac_match[] = {
{ .compatible = "amlogic,meson8b-dwmac" },
+ { .compatible = "amlogic,meson8m2-dwmac" },
{ .compatible = "amlogic,meson-gxbb-dwmac" },
{ }
};
@@ -349,7 +326,7 @@ MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
static struct platform_driver meson8b_dwmac_driver = {
.probe = meson8b_dwmac_probe,
- .remove = meson8b_dwmac_remove,
+ .remove = stmmac_pltfr_remove,
.driver = {
.name = "meson8b-dwmac",
.pm = &stmmac_pltfr_pm_ops,
@@ -359,5 +336,5 @@ static struct platform_driver meson8b_dwmac_driver = {
module_platform_driver(meson8b_dwmac_driver);
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
+MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 7761a26ec9c5..c7bff596c665 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -39,6 +39,7 @@
#define GMAC_HW_FEATURE0 0x0000011c
#define GMAC_HW_FEATURE1 0x00000120
#define GMAC_HW_FEATURE2 0x00000124
+#define GMAC_HW_FEATURE3 0x00000128
#define GMAC_MDIO_ADDR 0x00000200
#define GMAC_MDIO_DATA 0x00000204
#define GMAC_ADDR_HIGH(reg) (0x300 + reg * 8)
@@ -192,6 +193,9 @@ enum power_event {
#define GMAC_HW_FEAT_TXQCNT GENMASK(9, 6)
#define GMAC_HW_FEAT_RXQCNT GENMASK(3, 0)
+/* MAC HW features3 bitmap */
+#define GMAC_HW_FEAT_ASP GENMASK(29, 28)
+
/* MAC HW ADDR regs */
#define GMAC_HI_DCS GENMASK(18, 16)
#define GMAC_HI_DCS_SHIFT 16
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 63795ecafc8d..a3af92ebbca8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -20,6 +20,7 @@
#include <net/dsa.h>
#include "stmmac_pcs.h"
#include "dwmac4.h"
+#include "dwmac5.h"
static void dwmac4_core_init(struct mac_device_info *hw,
struct net_device *dev)
@@ -120,7 +121,7 @@ static void dwmac4_tx_queue_priority(struct mac_device_info *hw,
writel(value, ioaddr + base_register);
}
-static void dwmac4_tx_queue_routing(struct mac_device_info *hw,
+static void dwmac4_rx_queue_routing(struct mac_device_info *hw,
u8 packet, u32 queue)
{
void __iomem *ioaddr = hw->pcsr;
@@ -713,7 +714,7 @@ static const struct stmmac_ops dwmac4_ops = {
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
.tx_queue_prio = dwmac4_tx_queue_priority,
- .rx_queue_routing = dwmac4_tx_queue_routing,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
.prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
.prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
.set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
@@ -744,7 +745,7 @@ static const struct stmmac_ops dwmac410_ops = {
.rx_queue_enable = dwmac4_rx_queue_enable,
.rx_queue_prio = dwmac4_rx_queue_priority,
.tx_queue_prio = dwmac4_tx_queue_priority,
- .rx_queue_routing = dwmac4_tx_queue_routing,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
.prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
.prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
.set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
@@ -768,6 +769,40 @@ static const struct stmmac_ops dwmac410_ops = {
.set_filter = dwmac4_set_filter,
};
+static const struct stmmac_ops dwmac510_ops = {
+ .core_init = dwmac4_core_init,
+ .set_mac = stmmac_dwmac4_set_mac,
+ .rx_ipc = dwmac4_rx_ipc_enable,
+ .rx_queue_enable = dwmac4_rx_queue_enable,
+ .rx_queue_prio = dwmac4_rx_queue_priority,
+ .tx_queue_prio = dwmac4_tx_queue_priority,
+ .rx_queue_routing = dwmac4_rx_queue_routing,
+ .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms,
+ .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms,
+ .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight,
+ .map_mtl_to_dma = dwmac4_map_mtl_dma,
+ .config_cbs = dwmac4_config_cbs,
+ .dump_regs = dwmac4_dump_regs,
+ .host_irq_status = dwmac4_irq_status,
+ .host_mtl_irq_status = dwmac4_irq_mtl_status,
+ .flow_ctrl = dwmac4_flow_ctrl,
+ .pmt = dwmac4_pmt,
+ .set_umac_addr = dwmac4_set_umac_addr,
+ .get_umac_addr = dwmac4_get_umac_addr,
+ .set_eee_mode = dwmac4_set_eee_mode,
+ .reset_eee_mode = dwmac4_reset_eee_mode,
+ .set_eee_timer = dwmac4_set_eee_timer,
+ .set_eee_pls = dwmac4_set_eee_pls,
+ .pcs_ctrl_ane = dwmac4_ctrl_ane,
+ .pcs_rane = dwmac4_rane,
+ .pcs_get_adv_lp = dwmac4_get_adv_lp,
+ .debug = dwmac4_debug,
+ .set_filter = dwmac4_set_filter,
+ .safety_feat_config = dwmac5_safety_feat_config,
+ .safety_feat_irq_status = dwmac5_safety_feat_irq_status,
+ .safety_feat_dump = dwmac5_safety_feat_dump,
+};
+
struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins,
int perfect_uc_entries, int *synopsys_id)
{
@@ -808,7 +843,9 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins,
else
mac->dma = &dwmac4_dma_ops;
- if (*synopsys_id >= DWMAC_CORE_4_00)
+ if (*synopsys_id >= DWMAC_CORE_5_10)
+ mac->mac = &dwmac510_ops;
+ else if (*synopsys_id >= DWMAC_CORE_4_00)
mac->mac = &dwmac410_ops;
else
mac->mac = &dwmac4_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
index c728ffa095de..2a6521d33e43 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
@@ -389,6 +389,8 @@ static void dwmac4_rd_prepare_tso_tx_desc(struct dma_desc *p, int is_fs,
static void dwmac4_release_tx_desc(struct dma_desc *p, int mode)
{
+ p->des0 = 0;
+ p->des1 = 0;
p->des2 = 0;
p->des3 = 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index c110f6850ffa..d37d457306d1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -373,6 +373,12 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr,
/* IEEE 1588-2002 */
dma_cap->time_stamp = 0;
+
+ /* MAC HW feature3 */
+ hw_cap = readl(ioaddr + GMAC_HW_FEATURE3);
+
+ /* 5.10 Features */
+ dma_cap->asp = (hw_cap & GMAC_HW_FEAT_ASP) >> 28;
}
/* Enable/disable TSO feature and set MSS */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
new file mode 100644
index 000000000000..860de39999c7
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
+// stmmac Support for 5.xx Ethernet QoS cores
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include "common.h"
+#include "dwmac4.h"
+#include "dwmac5.h"
+
+struct dwmac5_error_desc {
+ bool valid;
+ const char *desc;
+ const char *detailed_desc;
+};
+
+#define STAT_OFF(field) offsetof(struct stmmac_safety_stats, field)
+
+static void dwmac5_log_error(struct net_device *ndev, u32 value, bool corr,
+ const char *module_name, const struct dwmac5_error_desc *desc,
+ unsigned long field_offset, struct stmmac_safety_stats *stats)
+{
+ unsigned long loc, mask;
+ u8 *bptr = (u8 *)stats;
+ unsigned long *ptr;
+
+ ptr = (unsigned long *)(bptr + field_offset);
+
+ mask = value;
+ for_each_set_bit(loc, &mask, 32) {
+ netdev_err(ndev, "Found %s error in %s: '%s: %s'\n", corr ?
+ "correctable" : "uncorrectable", module_name,
+ desc[loc].desc, desc[loc].detailed_desc);
+
+ /* Update counters */
+ ptr[loc]++;
+ }
+}
+
+static const struct dwmac5_error_desc dwmac5_mac_errors[32]= {
+ { true, "ATPES", "Application Transmit Interface Parity Check Error" },
+ { true, "TPES", "TSO Data Path Parity Check Error" },
+ { true, "RDPES", "Read Descriptor Parity Check Error" },
+ { true, "MPES", "MTL Data Path Parity Check Error" },
+ { true, "MTSPES", "MTL TX Status Data Path Parity Check Error" },
+ { true, "ARPES", "Application Receive Interface Data Path Parity Check Error" },
+ { true, "CWPES", "CSR Write Data Path Parity Check Error" },
+ { true, "ASRPES", "AXI Slave Read Data Path Parity Check Error" },
+ { true, "TTES", "TX FSM Timeout Error" },
+ { true, "RTES", "RX FSM Timeout Error" },
+ { true, "CTES", "CSR FSM Timeout Error" },
+ { true, "ATES", "APP FSM Timeout Error" },
+ { true, "PTES", "PTP FSM Timeout Error" },
+ { true, "T125ES", "TX125 FSM Timeout Error" },
+ { true, "R125ES", "RX125 FSM Timeout Error" },
+ { true, "RVCTES", "REV MDC FSM Timeout Error" },
+ { true, "MSTTES", "Master Read/Write Timeout Error" },
+ { true, "SLVTES", "Slave Read/Write Timeout Error" },
+ { true, "ATITES", "Application Timeout on ATI Interface Error" },
+ { true, "ARITES", "Application Timeout on ARI Interface Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { true, "FSMPES", "FSM State Parity Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_mac_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + MAC_DPP_FSM_INT_STATUS);
+ writel(value, ioaddr + MAC_DPP_FSM_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "MAC", dwmac5_mac_errors,
+ STAT_OFF(mac_errors), stats);
+}
+
+static const struct dwmac5_error_desc dwmac5_mtl_errors[32]= {
+ { true, "TXCES", "MTL TX Memory Error" },
+ { true, "TXAMS", "MTL TX Memory Address Mismatch Error" },
+ { true, "TXUES", "MTL TX Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 3 */
+ { true, "RXCES", "MTL RX Memory Error" },
+ { true, "RXAMS", "MTL RX Memory Address Mismatch Error" },
+ { true, "RXUES", "MTL RX Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 7 */
+ { true, "ECES", "MTL EST Memory Error" },
+ { true, "EAMS", "MTL EST Memory Address Mismatch Error" },
+ { true, "EUES", "MTL EST Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 11 */
+ { true, "RPCES", "MTL RX Parser Memory Error" },
+ { true, "RPAMS", "MTL RX Parser Memory Address Mismatch Error" },
+ { true, "RPUES", "MTL RX Parser Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 15 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 16 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 17 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 18 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 19 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 24 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_mtl_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + MTL_ECC_INT_STATUS);
+ writel(value, ioaddr + MTL_ECC_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "MTL", dwmac5_mtl_errors,
+ STAT_OFF(mtl_errors), stats);
+}
+
+static const struct dwmac5_error_desc dwmac5_dma_errors[32]= {
+ { true, "TCES", "DMA TSO Memory Error" },
+ { true, "TAMS", "DMA TSO Memory Address Mismatch Error" },
+ { true, "TUES", "DMA TSO Memory Error" },
+ { false, "UNKNOWN", "Unknown Error" }, /* 3 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 4 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 5 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 6 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 7 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 8 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 9 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 10 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 11 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 12 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 13 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 14 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 15 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 16 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 17 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 18 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 19 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 20 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 21 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 22 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 23 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 24 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 25 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 26 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 27 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 28 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 29 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 30 */
+ { false, "UNKNOWN", "Unknown Error" }, /* 31 */
+};
+
+static void dwmac5_handle_dma_err(struct net_device *ndev,
+ void __iomem *ioaddr, bool correctable,
+ struct stmmac_safety_stats *stats)
+{
+ u32 value;
+
+ value = readl(ioaddr + DMA_ECC_INT_STATUS);
+ writel(value, ioaddr + DMA_ECC_INT_STATUS);
+
+ dwmac5_log_error(ndev, value, correctable, "DMA", dwmac5_dma_errors,
+ STAT_OFF(dma_errors), stats);
+}
+
+int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp)
+{
+ u32 value;
+
+ if (!asp)
+ return -EINVAL;
+
+ /* 1. Enable Safety Features */
+ value = readl(ioaddr + MTL_ECC_CONTROL);
+ value |= TSOEE; /* TSO ECC */
+ value |= MRXPEE; /* MTL RX Parser ECC */
+ value |= MESTEE; /* MTL EST ECC */
+ value |= MRXEE; /* MTL RX FIFO ECC */
+ value |= MTXEE; /* MTL TX FIFO ECC */
+ writel(value, ioaddr + MTL_ECC_CONTROL);
+
+ /* 2. Enable MTL Safety Interrupts */
+ value = readl(ioaddr + MTL_ECC_INT_ENABLE);
+ value |= RPCEIE; /* RX Parser Memory Correctable Error */
+ value |= ECEIE; /* EST Memory Correctable Error */
+ value |= RXCEIE; /* RX Memory Correctable Error */
+ value |= TXCEIE; /* TX Memory Correctable Error */
+ writel(value, ioaddr + MTL_ECC_INT_ENABLE);
+
+ /* 3. Enable DMA Safety Interrupts */
+ value = readl(ioaddr + DMA_ECC_INT_ENABLE);
+ value |= TCEIE; /* TSO Memory Correctable Error */
+ writel(value, ioaddr + DMA_ECC_INT_ENABLE);
+
+ /* Only ECC Protection for External Memory feature is selected */
+ if (asp <= 0x1)
+ return 0;
+
+ /* 5. Enable Parity and Timeout for FSM */
+ value = readl(ioaddr + MAC_FSM_CONTROL);
+ value |= PRTYEN; /* FSM Parity Feature */
+ value |= TMOUTEN; /* FSM Timeout Feature */
+ writel(value, ioaddr + MAC_FSM_CONTROL);
+
+ /* 4. Enable Data Parity Protection */
+ value = readl(ioaddr + MTL_DPP_CONTROL);
+ value |= EDPP;
+ writel(value, ioaddr + MTL_DPP_CONTROL);
+
+ /*
+ * All the Automotive Safety features are selected without the "Parity
+ * Port Enable for external interface" feature.
+ */
+ if (asp <= 0x2)
+ return 0;
+
+ value |= EPSI;
+ writel(value, ioaddr + MTL_DPP_CONTROL);
+ return 0;
+}
+
+bool dwmac5_safety_feat_irq_status(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats)
+{
+ bool ret = false, err, corr;
+ u32 mtl, dma;
+
+ if (!asp)
+ return false;
+
+ mtl = readl(ioaddr + MTL_SAFETY_INT_STATUS);
+ dma = readl(ioaddr + DMA_SAFETY_INT_STATUS);
+
+ err = (mtl & MCSIS) || (dma & MCSIS);
+ corr = false;
+ if (err) {
+ dwmac5_handle_mac_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ err = (mtl & (MEUIS | MECIS)) || (dma & (MSUIS | MSCIS));
+ corr = (mtl & MECIS) || (dma & MSCIS);
+ if (err) {
+ dwmac5_handle_mtl_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ err = dma & (DEUIS | DECIS);
+ corr = dma & DECIS;
+ if (err) {
+ dwmac5_handle_dma_err(ndev, ioaddr, corr, stats);
+ ret |= !corr;
+ }
+
+ return ret;
+}
+
+static const struct dwmac5_error {
+ const struct dwmac5_error_desc *desc;
+} dwmac5_all_errors[] = {
+ { dwmac5_mac_errors },
+ { dwmac5_mtl_errors },
+ { dwmac5_dma_errors },
+};
+
+const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count)
+{
+ int module = index / 32, offset = index % 32;
+ unsigned long *ptr = (unsigned long *)stats;
+
+ if (module >= ARRAY_SIZE(dwmac5_all_errors))
+ return NULL;
+ if (!dwmac5_all_errors[module].desc[offset].valid)
+ return NULL;
+ if (count)
+ *count = *(ptr + index);
+ return dwmac5_all_errors[module].desc[offset].desc;
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.h b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
new file mode 100644
index 000000000000..a0d2c44711b9
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates.
+// stmmac Support for 5.xx Ethernet QoS cores
+
+#ifndef __DWMAC5_H__
+#define __DWMAC5_H__
+
+#define MAC_DPP_FSM_INT_STATUS 0x00000140
+#define MAC_AXI_SLV_DPE_ADDR_STATUS 0x00000144
+#define MAC_FSM_CONTROL 0x00000148
+#define PRTYEN BIT(1)
+#define TMOUTEN BIT(0)
+
+#define MTL_ECC_CONTROL 0x00000cc0
+#define TSOEE BIT(4)
+#define MRXPEE BIT(3)
+#define MESTEE BIT(2)
+#define MRXEE BIT(1)
+#define MTXEE BIT(0)
+
+#define MTL_SAFETY_INT_STATUS 0x00000cc4
+#define MCSIS BIT(31)
+#define MEUIS BIT(1)
+#define MECIS BIT(0)
+#define MTL_ECC_INT_ENABLE 0x00000cc8
+#define RPCEIE BIT(12)
+#define ECEIE BIT(8)
+#define RXCEIE BIT(4)
+#define TXCEIE BIT(0)
+#define MTL_ECC_INT_STATUS 0x00000ccc
+#define MTL_DPP_CONTROL 0x00000ce0
+#define EPSI BIT(2)
+#define OPE BIT(1)
+#define EDPP BIT(0)
+
+#define DMA_SAFETY_INT_STATUS 0x00001080
+#define MSUIS BIT(29)
+#define MSCIS BIT(28)
+#define DEUIS BIT(1)
+#define DECIS BIT(0)
+#define DMA_ECC_INT_ENABLE 0x00001084
+#define TCEIE BIT(0)
+#define DMA_ECC_INT_STATUS 0x00001088
+
+int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp);
+bool dwmac5_safety_feat_irq_status(struct net_device *ndev,
+ void __iomem *ioaddr, unsigned int asp,
+ struct stmmac_safety_stats *stats);
+const char *dwmac5_safety_feat_dump(struct stmmac_safety_stats *stats,
+ int index, unsigned long *count);
+
+#endif /* __DWMAC5_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index a916e13624eb..da50451f8999 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -58,6 +58,7 @@ struct stmmac_tx_queue {
unsigned int dirty_tx;
dma_addr_t dma_tx_phy;
u32 tx_tail_addr;
+ u32 mss;
};
struct stmmac_rx_queue {
@@ -113,6 +114,7 @@ struct stmmac_priv {
int mii_irq[PHY_MAX_ADDR];
struct stmmac_extra_stats xstats ____cacheline_aligned_in_smp;
+ struct stmmac_safety_stats sstats;
struct plat_stmmacenet_data *plat;
struct dma_features dma_cap;
struct stmmac_counters mmc;
@@ -138,13 +140,23 @@ struct stmmac_priv {
spinlock_t ptp_lock;
void __iomem *mmcaddr;
void __iomem *ptpaddr;
- u32 mss;
#ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_dir;
struct dentry *dbgfs_rings_status;
struct dentry *dbgfs_dma_cap;
#endif
+
+ unsigned long state;
+ struct workqueue_struct *wq;
+ struct work_struct service_task;
+};
+
+enum stmmac_state {
+ STMMAC_DOWN,
+ STMMAC_RESET_REQUESTED,
+ STMMAC_RESETING,
+ STMMAC_SERVICE_SCHED,
};
int stmmac_mdio_unregister(struct net_device *ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index af30b4857c3b..2c6ed47704fc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -523,11 +523,23 @@ stmmac_set_pauseparam(struct net_device *netdev,
static void stmmac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *dummy, u64 *data)
{
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
struct stmmac_priv *priv = netdev_priv(dev);
u32 rx_queues_count = priv->plat->rx_queues_to_use;
u32 tx_queues_count = priv->plat->tx_queues_to_use;
+ unsigned long count;
int i, j = 0;
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ if (dump(&priv->sstats, i, &count))
+ data[j++] = count;
+ }
+ }
+
/* Update the DMA HW counters for dwmac10/100 */
if (priv->hw->dma->dma_diagnostic_fr)
priv->hw->dma->dma_diagnostic_fr(&dev->stats,
@@ -569,7 +581,9 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
static int stmmac_get_sset_count(struct net_device *netdev, int sset)
{
struct stmmac_priv *priv = netdev_priv(netdev);
- int len;
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
+ int i, len, safety_len = 0;
switch (sset) {
case ETH_SS_STATS:
@@ -577,6 +591,16 @@ static int stmmac_get_sset_count(struct net_device *netdev, int sset)
if (priv->dma_cap.rmon)
len += STMMAC_MMC_STATS_LEN;
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ if (dump(&priv->sstats, i, NULL))
+ safety_len++;
+ }
+
+ len += safety_len;
+ }
return len;
default:
@@ -589,9 +613,22 @@ static void stmmac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
int i;
u8 *p = data;
struct stmmac_priv *priv = netdev_priv(dev);
+ const char *(*dump)(struct stmmac_safety_stats *stats, int index,
+ unsigned long *count);
switch (stringset) {
case ETH_SS_STATS:
+ if (priv->dma_cap.asp && priv->hw->mac->safety_feat_dump) {
+ dump = priv->hw->mac->safety_feat_dump;
+ for (i = 0; i < STMMAC_SAFETY_FEAT_SIZE; i++) {
+ const char *desc = dump(&priv->sstats, i, NULL);
+
+ if (desc) {
+ memcpy(p, desc, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ }
+ }
if (priv->dma_cap.rmon)
for (i = 0; i < STMMAC_MMC_STATS_LEN; i++) {
memcpy(p, stmmac_mmc[i].stat_string,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 7ad841434ec8..9a16931ce39d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -57,36 +57,36 @@
/* Module parameters */
#define TX_TIMEO 5000
static int watchdog = TX_TIMEO;
-module_param(watchdog, int, S_IRUGO | S_IWUSR);
+module_param(watchdog, int, 0644);
MODULE_PARM_DESC(watchdog, "Transmit timeout in milliseconds (default 5s)");
static int debug = -1;
-module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Message Level (-1: default, 0: no output, 16: all)");
static int phyaddr = -1;
-module_param(phyaddr, int, S_IRUGO);
+module_param(phyaddr, int, 0444);
MODULE_PARM_DESC(phyaddr, "Physical device address");
#define STMMAC_TX_THRESH (DMA_TX_SIZE / 4)
#define STMMAC_RX_THRESH (DMA_RX_SIZE / 4)
static int flow_ctrl = FLOW_OFF;
-module_param(flow_ctrl, int, S_IRUGO | S_IWUSR);
+module_param(flow_ctrl, int, 0644);
MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]");
static int pause = PAUSE_TIME;
-module_param(pause, int, S_IRUGO | S_IWUSR);
+module_param(pause, int, 0644);
MODULE_PARM_DESC(pause, "Flow Control Pause Time");
#define TC_DEFAULT 64
static int tc = TC_DEFAULT;
-module_param(tc, int, S_IRUGO | S_IWUSR);
+module_param(tc, int, 0644);
MODULE_PARM_DESC(tc, "DMA threshold control value");
#define DEFAULT_BUFSIZE 1536
static int buf_sz = DEFAULT_BUFSIZE;
-module_param(buf_sz, int, S_IRUGO | S_IWUSR);
+module_param(buf_sz, int, 0644);
MODULE_PARM_DESC(buf_sz, "DMA buffer size");
#define STMMAC_RX_COPYBREAK 256
@@ -97,7 +97,7 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE |
#define STMMAC_DEFAULT_LPI_TIMER 1000
static int eee_timer = STMMAC_DEFAULT_LPI_TIMER;
-module_param(eee_timer, int, S_IRUGO | S_IWUSR);
+module_param(eee_timer, int, 0644);
MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
#define STMMAC_LPI_T(x) (jiffies + msecs_to_jiffies(x))
@@ -105,7 +105,7 @@ MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec");
* but allow user to force to use the chain instead of the ring
*/
static unsigned int chain_mode;
-module_param(chain_mode, int, S_IRUGO);
+module_param(chain_mode, int, 0444);
MODULE_PARM_DESC(chain_mode, "To use chain instead of ring mode");
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
@@ -196,6 +196,20 @@ static void stmmac_start_all_queues(struct stmmac_priv *priv)
netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue));
}
+static void stmmac_service_event_schedule(struct stmmac_priv *priv)
+{
+ if (!test_bit(STMMAC_DOWN, &priv->state) &&
+ !test_and_set_bit(STMMAC_SERVICE_SCHED, &priv->state))
+ queue_work(priv->wq, &priv->service_task);
+}
+
+static void stmmac_global_err(struct stmmac_priv *priv)
+{
+ netif_carrier_off(priv->dev);
+ set_bit(STMMAC_RESET_REQUESTED, &priv->state);
+ stmmac_service_event_schedule(priv);
+}
+
/**
* stmmac_clk_csr_set - dynamically set the MDC clock
* @priv: driver private structure
@@ -1355,6 +1369,7 @@ static int init_dma_tx_desc_rings(struct net_device *dev)
tx_q->dirty_tx = 0;
tx_q->cur_tx = 0;
+ tx_q->mss = 0;
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue));
}
@@ -1843,6 +1858,11 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue)
if (unlikely(status & tx_dma_own))
break;
+ /* Make sure descriptor fields are read after reading
+ * the own bit.
+ */
+ dma_rmb();
+
/* Just consider the last segment and ...*/
if (likely(!(status & tx_not_ls))) {
/* ... verify the status error condition */
@@ -1946,6 +1966,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan)
(i == DMA_TX_SIZE - 1));
tx_q->dirty_tx = 0;
tx_q->cur_tx = 0;
+ tx_q->mss = 0;
netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan));
stmmac_start_tx_dma(priv, chan);
@@ -1993,6 +2014,22 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode,
}
}
+static bool stmmac_safety_feat_interrupt(struct stmmac_priv *priv)
+{
+ bool ret = false;
+
+ /* Safety features are only available in cores >= 5.10 */
+ if (priv->synopsys_id < DWMAC_CORE_5_10)
+ return ret;
+ if (priv->hw->mac->safety_feat_irq_status)
+ ret = priv->hw->mac->safety_feat_irq_status(priv->dev,
+ priv->ioaddr, priv->dma_cap.asp, &priv->sstats);
+
+ if (ret)
+ stmmac_global_err(priv);
+ return ret;
+}
+
/**
* stmmac_dma_interrupt - DMA ISR
* @priv: driver private structure
@@ -2430,7 +2467,7 @@ static void stmmac_mac_config_rx_queues_routing(struct stmmac_priv *priv)
continue;
packet = priv->plat->rx_queues_cfg[queue].pkt_route;
- priv->hw->mac->rx_queue_prio(priv->hw, packet, queue);
+ priv->hw->mac->rx_queue_routing(priv->hw, packet, queue);
}
}
@@ -2482,6 +2519,17 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv)
stmmac_mac_config_rx_queues_routing(priv);
}
+static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
+{
+ if (priv->hw->mac->safety_feat_config && priv->dma_cap.asp) {
+ netdev_info(priv->dev, "Enabling Safety Features\n");
+ priv->hw->mac->safety_feat_config(priv->ioaddr,
+ priv->dma_cap.asp);
+ } else {
+ netdev_info(priv->dev, "No Safety Features support found\n");
+ }
+}
+
/**
* stmmac_hw_setup - setup mac in a usable state.
* @dev : pointer to the device structure.
@@ -2533,6 +2581,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
if (priv->synopsys_id >= DWMAC_CORE_4_00)
stmmac_mtl_configuration(priv);
+ /* Initialize Safety Features */
+ if (priv->synopsys_id >= DWMAC_CORE_5_10)
+ stmmac_safety_feat_configuration(priv);
+
ret = priv->hw->mac->rx_ipc(priv->hw);
if (!ret) {
netdev_warn(priv->dev, "RX IPC Checksum Offload disabled\n");
@@ -2632,7 +2684,6 @@ static int stmmac_open(struct net_device *dev)
priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
priv->rx_copybreak = STMMAC_RX_COPYBREAK;
- priv->mss = 0;
ret = alloc_dma_desc_resources(priv);
if (ret < 0) {
@@ -2793,6 +2844,7 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des,
while (tmp_len > 0) {
tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
desc = tx_q->dma_tx + tx_q->cur_tx;
desc->des0 = cpu_to_le32(des + (total_len - tmp_len));
@@ -2872,11 +2924,12 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
mss = skb_shinfo(skb)->gso_size;
/* set new MSS value if needed */
- if (mss != priv->mss) {
+ if (mss != tx_q->mss) {
mss_desc = tx_q->dma_tx + tx_q->cur_tx;
priv->hw->desc->set_mss(mss_desc, mss);
- priv->mss = mss;
+ tx_q->mss = mss;
tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
}
if (netif_msg_tx_queued(priv)) {
@@ -2887,6 +2940,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
+ WARN_ON(tx_q->tx_skbuff[first_entry]);
desc = tx_q->dma_tx + first_entry;
first = desc;
@@ -2926,7 +2980,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des;
tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag);
- tx_q->tx_skbuff[tx_q->cur_tx] = NULL;
tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true;
}
@@ -2980,14 +3033,21 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len));
/* If context desc is used to change MSS */
- if (mss_desc)
+ if (mss_desc) {
+ /* Make sure that first descriptor has been completely
+ * written, including its own bit. This is because MSS is
+ * actually before first descriptor, so we need to make
+ * sure that MSS's own bit is the last thing written.
+ */
+ dma_wmb();
priv->hw->desc->set_tx_owner(mss_desc);
+ }
/* The own bit must be the latest setting done when prepare the
* descriptor and then barrier is needed to make sure that
* all is coherent before granting the DMA engine.
*/
- dma_wmb();
+ wmb();
if (netif_msg_pktdata(priv)) {
pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n",
@@ -3062,6 +3122,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
entry = tx_q->cur_tx;
first_entry = entry;
+ WARN_ON(tx_q->tx_skbuff[first_entry]);
csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL);
@@ -3090,6 +3151,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
bool last_segment = (i == (nfrags - 1));
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
+ WARN_ON(tx_q->tx_skbuff[entry]);
if (likely(priv->extend_desc))
desc = (struct dma_desc *)(tx_q->dma_etx + entry);
@@ -3101,8 +3163,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (dma_mapping_error(priv->device, des))
goto dma_map_err; /* should reuse desc w/o issues */
- tx_q->tx_skbuff[entry] = NULL;
-
tx_q->tx_skbuff_dma[entry].buf = des;
if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
desc->des0 = cpu_to_le32(des);
@@ -3211,7 +3271,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
* descriptor and then barrier is needed to make sure that
* all is coherent before granting the DMA engine.
*/
- dma_wmb();
+ wmb();
}
netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len);
@@ -3572,12 +3632,8 @@ static int stmmac_poll(struct napi_struct *napi, int budget)
static void stmmac_tx_timeout(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- u32 tx_count = priv->plat->tx_queues_to_use;
- u32 chan;
- /* Clear Tx resources and restart transmitting again */
- for (chan = 0; chan < tx_count; chan++)
- stmmac_tx_err(priv, chan);
+ stmmac_global_err(priv);
}
/**
@@ -3701,6 +3757,13 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
+ /* Check if adapter is up */
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return IRQ_HANDLED;
+ /* Check if a fatal error happened */
+ if (stmmac_safety_feat_interrupt(priv))
+ return IRQ_HANDLED;
+
/* To handle GMAC own interrupts */
if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) {
int status = priv->hw->mac->host_irq_status(priv->hw,
@@ -3986,7 +4049,7 @@ static int stmmac_init_fs(struct net_device *dev)
/* Entry to report DMA RX/TX rings */
priv->dbgfs_rings_status =
- debugfs_create_file("descriptors_status", S_IRUGO,
+ debugfs_create_file("descriptors_status", 0444,
priv->dbgfs_dir, dev,
&stmmac_rings_status_fops);
@@ -3998,9 +4061,9 @@ static int stmmac_init_fs(struct net_device *dev)
}
/* Entry to report the DMA HW features */
- priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
- priv->dbgfs_dir,
- dev, &stmmac_dma_cap_fops);
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", 0444,
+ priv->dbgfs_dir,
+ dev, &stmmac_dma_cap_fops);
if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
netdev_err(priv->dev, "ERROR creating stmmac MMC debugfs file\n");
@@ -4036,6 +4099,37 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_set_mac_address = stmmac_set_mac_address,
};
+static void stmmac_reset_subtask(struct stmmac_priv *priv)
+{
+ if (!test_and_clear_bit(STMMAC_RESET_REQUESTED, &priv->state))
+ return;
+ if (test_bit(STMMAC_DOWN, &priv->state))
+ return;
+
+ netdev_err(priv->dev, "Reset adapter.\n");
+
+ rtnl_lock();
+ netif_trans_update(priv->dev);
+ while (test_and_set_bit(STMMAC_RESETING, &priv->state))
+ usleep_range(1000, 2000);
+
+ set_bit(STMMAC_DOWN, &priv->state);
+ dev_close(priv->dev);
+ dev_open(priv->dev);
+ clear_bit(STMMAC_DOWN, &priv->state);
+ clear_bit(STMMAC_RESETING, &priv->state);
+ rtnl_unlock();
+}
+
+static void stmmac_service_task(struct work_struct *work)
+{
+ struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
+ service_task);
+
+ stmmac_reset_subtask(priv);
+ clear_bit(STMMAC_SERVICE_SCHED, &priv->state);
+}
+
/**
* stmmac_hw_init - Init the MAC device
* @priv: driver private structure
@@ -4197,6 +4291,15 @@ int stmmac_dvr_probe(struct device *device,
/* Verify driver arguments */
stmmac_verify_args();
+ /* Allocate workqueue */
+ priv->wq = create_singlethread_workqueue("stmmac_wq");
+ if (!priv->wq) {
+ dev_err(priv->device, "failed to create workqueue\n");
+ goto error_wq;
+ }
+
+ INIT_WORK(&priv->service_task, stmmac_service_task);
+
/* Override with kernel parameters if supplied XXX CRS XXX
* this needs to have multiple instances
*/
@@ -4327,6 +4430,8 @@ error_mdio_register:
netif_napi_del(&rx_q->napi);
}
error_hw_init:
+ destroy_workqueue(priv->wq);
+error_wq:
free_netdev(ndev);
return ret;
@@ -4359,6 +4464,7 @@ int stmmac_dvr_remove(struct device *dev)
priv->hw->pcs != STMMAC_PCS_TBI &&
priv->hw->pcs != STMMAC_PCS_RTBI)
stmmac_mdio_unregister(ndev);
+ destroy_workqueue(priv->wq);
free_netdev(ndev);
return 0;
@@ -4436,6 +4542,7 @@ static void stmmac_reset_queues_param(struct stmmac_priv *priv)
tx_q->cur_tx = 0;
tx_q->dirty_tx = 0;
+ tx_q->mss = 0;
}
}
@@ -4481,11 +4588,6 @@ int stmmac_resume(struct device *dev)
stmmac_reset_queues_param(priv);
- /* reset private mss value to force mss context settings at
- * next tso xmit (only used for gmac4).
- */
- priv->mss = 0;
-
stmmac_clear_descriptors(priv);
stmmac_hw_setup(ndev, false);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 05f122b8424a..ebd3e5ffa73c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -135,13 +135,14 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
* stmmac_mtl_setup - parse DT parameters for multiple queues configuration
* @pdev: platform device
*/
-static void stmmac_mtl_setup(struct platform_device *pdev,
- struct plat_stmmacenet_data *plat)
+static int stmmac_mtl_setup(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat)
{
struct device_node *q_node;
struct device_node *rx_node;
struct device_node *tx_node;
u8 queue = 0;
+ int ret = 0;
/* For backwards-compatibility with device trees that don't have any
* snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back
@@ -159,12 +160,12 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0);
if (!rx_node)
- return;
+ return ret;
tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0);
if (!tx_node) {
of_node_put(rx_node);
- return;
+ return ret;
}
/* Processing RX queues common config */
@@ -220,6 +221,11 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
queue++;
}
+ if (queue != plat->rx_queues_to_use) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Not all RX queues were configured\n");
+ goto out;
+ }
/* Processing TX queues common config */
if (of_property_read_u32(tx_node, "snps,tx-queues-to-use",
@@ -281,10 +287,18 @@ static void stmmac_mtl_setup(struct platform_device *pdev,
queue++;
}
+ if (queue != plat->tx_queues_to_use) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Not all TX queues were configured\n");
+ goto out;
+ }
+out:
of_node_put(rx_node);
of_node_put(tx_node);
of_node_put(q_node);
+
+ return ret;
}
/**
@@ -376,6 +390,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
struct device_node *np = pdev->dev.of_node;
struct plat_stmmacenet_data *plat;
struct stmmac_dma_cfg *dma_cfg;
+ int rc;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
@@ -402,8 +417,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
/* To Configure PHY by using all device-tree supported properties */
- if (stmmac_dt_phy(plat, np, &pdev->dev))
- return ERR_PTR(-ENODEV);
+ rc = stmmac_dt_phy(plat, np, &pdev->dev);
+ if (rc)
+ return ERR_PTR(rc);
of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
@@ -499,7 +515,11 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
plat->axi = stmmac_axi_setup(pdev);
- stmmac_mtl_setup(pdev, plat);
+ rc = stmmac_mtl_setup(pdev, plat);
+ if (rc) {
+ stmmac_remove_config_dt(pdev, plat);
+ return ERR_PTR(rc);
+ }
/* clock setup */
plat->stmmac_clk = devm_clk_get(&pdev->dev,