summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c227
3 files changed, 237 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 71c1a21ba969..9d8c9347893f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -70,6 +70,15 @@ config DWMAC_ANARION
This selects the Anarion SoC glue layer support for the stmmac driver.
+config DWMAC_BT1
+ tristate "Baikal-T1 GMAC/xGMAC support"
+ depends on OF && (MIPS_BAIKAL_T1 || COMPILE_TEST)
+ help
+ Support for Baikal-T1 (x)GMAC Ethernet controller.
+
+ This selects the Baikal-T1 platform specific glue layer of the
+ STMMAC driver.
+
config DWMAC_EIC7700
tristate "Support for Eswin eic7700 ethernet driver"
depends on OF && HAS_DMA && ARCH_ESWIN || COMPILE_TEST
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index cd3c62b4a964..a63522ee0b38 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -14,6 +14,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
+obj-$(CONFIG_DWMAC_BT1) += dwmac-bt1.o
obj-$(CONFIG_DWMAC_EIC7700) += dwmac-eic7700.o
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c
new file mode 100644
index 000000000000..d14b233d13c2
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Baikal-T1 GMAC driver
+ *
+ * Copyright (C) 2022 BAIKAL ELECTRONICS, JSC
+ */
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pcs/pcs-xpcs.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#include "stmmac.h"
+#include "stmmac_platform.h"
+
+struct bt1_xgmac {
+ struct device *dev;
+};
+
+typedef int (*bt1_xgmac_plat_init)(struct bt1_xgmac *, struct plat_stmmacenet_data *);
+
+static void bt1_gmac_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,
+ unsigned long *interfaces)
+{
+ phy_interface_set_rgmii(interfaces);
+}
+
+static int bt1_gmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
+{
+ long rate;
+ int ret;
+
+ if (!phy_interface_mode_is_rgmii(interface))
+ return -EINVAL;
+
+ rate = rgmii_clock(speed);
+ if (rate < 0)
+ return -EINVAL;
+
+ /* Tx clock must be gated to successfully update the rate */
+ clk_disable_unprepare(clk_tx_i);
+
+ /* Clock is running with double rate (5 MHz, 50 MHz and 250 MHz) */
+ ret = clk_set_rate(clk_tx_i, 2 * rate);
+ if (ret)
+ goto err_en_clk;
+
+ return clk_prepare_enable(clk_tx_i);
+
+err_en_clk:
+ clk_prepare_enable(clk_tx_i);
+
+ return ret;
+}
+
+static int bt1_gmac_plat_data_init(struct bt1_xgmac *btxg,
+ struct plat_stmmacenet_data *plat)
+{
+ phy_interface_t interface;
+ int phyif;
+
+ plat->core_type = DWMAC_CORE_GMAC;
+ plat->host_dma_width = 32;
+ plat->tx_fifo_size = SZ_16K;
+ plat->rx_fifo_size = SZ_16K;
+ plat->enh_desc = 1;
+ plat->tx_coe = 1;
+ plat->rx_coe = 1;
+ plat->pmt = 1;
+ plat->unicast_filter_entries = 8;
+ plat->multicast_filter_bins = 0;
+ plat->set_clk_tx_rate = bt1_gmac_set_clk_tx_rate;
+ plat->get_interfaces = bt1_gmac_get_interfaces;
+ plat->mdio_bus_data->needs_reset = true;
+ plat->ngpios = 2;
+
+ phyif = stmmac_get_phy_intf_sel(plat->phy_interface);
+ if (phyif != PHY_INTF_SEL_RGMII) {
+ dev_err(btxg->dev, "Unsupported PHY interface specified\n");
+ return -EINVAL;
+ }
+
+ interface = phy_fix_phy_mode_for_mac_delays(plat->phy_interface, true, false);
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ dev_err(btxg->dev, "Unsupported RGMII mode specified\n");
+ return -EINVAL;
+ }
+
+ plat->phy_interface = interface;
+
+ return 0;
+}
+
+static void bt1_xgmac_get_interfaces(struct stmmac_priv *priv, void *bsp_priv,
+ unsigned long *interfaces)
+{
+ __set_bit(PHY_INTERFACE_MODE_XGMII, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASEX, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_XAUI, interfaces);
+}
+
+static struct phylink_pcs *bt1_xgmac_select_pcs(struct stmmac_priv *priv,
+ phy_interface_t interface)
+{
+ return xpcs_to_phylink_pcs(priv->hw->xpcs);
+}
+
+static int bt1_xgmac_plat_data_init(struct bt1_xgmac *btxg,
+ struct plat_stmmacenet_data *plat)
+{
+ /* Note Disabling SPH improves the network performance for about
+ * 300Mbit/s. Might be due to the DMA-syncing for two buffers.
+ * Also SPH consumes twice the normal memory especially if there
+ * is a single page-per-buffer allocated.
+ */
+ plat->core_type = DWMAC_CORE_XGMAC;
+ plat->host_dma_width = 40;
+ plat->tx_fifo_size = SZ_32K;
+ plat->rx_fifo_size = SZ_32K;
+ plat->tx_coe = 1;
+ plat->rx_coe = 1;
+ plat->rss_en = 1; /* & cap.rssen */
+ plat->unicast_filter_entries = 8;
+ plat->multicast_filter_bins = 64;
+ plat->select_pcs = bt1_xgmac_select_pcs;
+ plat->get_interfaces = bt1_xgmac_get_interfaces;
+ plat->flags |= STMMAC_FLAG_TSO_EN | /* & cap.tsoen */
+ STMMAC_FLAG_TSO_FULL | /* all channels TSO-capable */
+ STMMAC_FLAG_SPH_DISABLE | /* & cap.sph */
+ STMMAC_FLAG_MULTI_MSI_EN;
+
+ /* Attached XPCS supports 10GBase-KR/KX4 modes */
+ switch (plat->phy_interface) {
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_10GBASEX:
+ case PHY_INTERFACE_MODE_XAUI:
+ break;
+ default:
+ dev_err(btxg->dev, "Unsupported PHY interface specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bt1_xgmac_data_init(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat)
+{
+ bt1_xgmac_plat_init plat_init;
+ struct bt1_xgmac *btxg;
+ int ret;
+
+ btxg = devm_kzalloc(&pdev->dev, sizeof(*btxg), GFP_KERNEL);
+ if (!btxg)
+ return -ENOMEM;
+
+ btxg->dev = &pdev->dev;
+
+ plat->bsp_priv = btxg;
+ plat->clk_tx_i = devm_clk_get_enabled(&pdev->dev, "tx");
+ if (IS_ERR(plat->clk_tx_i))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat->clk_tx_i),
+ "Failed to get tx clock\n");
+
+ plat_init = device_get_match_data(&pdev->dev);
+ if (plat_init) {
+ ret = plat_init(btxg, plat);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int bt1_xgmac_probe(struct platform_device *pdev)
+{
+ struct stmmac_resources stmmac_res;
+ struct plat_stmmacenet_data *plat;
+ int ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return ret;
+
+ plat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat), "DT config failed\n");
+
+ ret = bt1_xgmac_data_init(pdev, plat);
+ if (ret)
+ return ret;
+
+ ret = devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id bt1_xgmac_match[] = {
+ { .compatible = "baikal,bt1-gmac", .data = (void *)bt1_gmac_plat_data_init },
+ { .compatible = "baikal,bt1-xgmac", .data = (void *)bt1_xgmac_plat_data_init },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bt1_xgmac_match);
+
+static struct platform_driver bt1_xgmac_driver = {
+ .probe = bt1_xgmac_probe,
+ .driver = {
+ .name = "bt1-xgmac",
+ .pm = &stmmac_pltfr_pm_ops,
+ .of_match_table = of_match_ptr(bt1_xgmac_match),
+ },
+};
+module_platform_driver(bt1_xgmac_driver);
+
+MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 GMAC/XGMAC glue driver");
+MODULE_LICENSE("GPL v2");