diff options
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-bt1.c | 227 |
3 files changed, 237 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 71c1a21ba9690..9d8c9347893f8 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 cd3c62b4a9646..a63522ee0b385 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 0000000000000..d14b233d13c2c --- /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"); |
