]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: stmmac: dwmac-s32: add basic NXP S32G/S32R glue driver
authorJan Petrous (OSS) <jan.petrous@oss.nxp.com>
Thu, 5 Dec 2024 16:43:11 +0000 (17:43 +0100)
committerJakub Kicinski <kuba@kernel.org>
Tue, 10 Dec 2024 02:36:03 +0000 (18:36 -0800)
NXP S32G2xx/S32G3xx and S32R45 are automotive grade SoCs
that integrate one or two Synopsys DWMAC 5.10/5.20 IPs.

The basic driver supports only RGMII interface.

Signed-off-by: Jan Petrous (OSS) <jan.petrous@oss.nxp.com>
Link: https://patch.msgid.link/20241205-upstream_s32cc_gmac-v8-14-ec1d180df815@oss.nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c [new file with mode: 0644]

index 6658536a4e1710ed16d5d5aefd0378399c9fd2e9..4cc85a36a1ab62c16d2b5dac57862606e12e78b4 100644 (file)
@@ -154,6 +154,18 @@ config DWMAC_RZN1
          the stmmac device driver. This support can make use of a custom MII
          converter PCS device.
 
+config DWMAC_S32
+       tristate "NXP S32G/S32R GMAC support"
+       default ARCH_S32
+       depends on OF && (ARCH_S32 || COMPILE_TEST)
+       help
+         Support for ethernet controller on NXP S32CC SOCs.
+
+         This selects NXP SoC glue layer support for the stmmac
+         device driver. This driver is used for the S32CC series
+         SOCs GMAC ethernet controller, ie. S32G2xx, S32G3xx and
+         S32R45.
+
 config DWMAC_SOCFPGA
        tristate "SOCFPGA dwmac support"
        default ARCH_INTEL_SOCFPGA
index 2389fd261344658b82cf8b6bc3db8c427afe2cae..b26f0e79c2b3214c6f606b06c7f1bfa449d90ba6 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_DWMAC_MESON)     += dwmac-meson.o dwmac-meson8b.o
 obj-$(CONFIG_DWMAC_QCOM_ETHQOS)        += dwmac-qcom-ethqos.o
 obj-$(CONFIG_DWMAC_ROCKCHIP)   += dwmac-rk.o
 obj-$(CONFIG_DWMAC_RZN1)       += dwmac-rzn1.o
+obj-$(CONFIG_DWMAC_S32)                += dwmac-s32.o
 obj-$(CONFIG_DWMAC_SOCFPGA)    += dwmac-altr-socfpga.o
 obj-$(CONFIG_DWMAC_STARFIVE)   += dwmac-starfive.o
 obj-$(CONFIG_DWMAC_STI)                += dwmac-sti.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c
new file mode 100644 (file)
index 0000000..9cc0e58
--- /dev/null
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP S32G/R GMAC glue layer
+ *
+ * Copyright 2019-2024 NXP
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/ethtool.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_mdio.h>
+#include <linux/of_address.h>
+#include <linux/phy.h>
+#include <linux/phylink.h>
+#include <linux/platform_device.h>
+#include <linux/stmmac.h>
+
+#include "stmmac_platform.h"
+
+#define GMAC_INTF_RATE_125M    125000000       /* 125MHz */
+
+/* SoC PHY interface control register */
+#define PHY_INTF_SEL_MII       0x00
+#define PHY_INTF_SEL_SGMII     0x01
+#define PHY_INTF_SEL_RGMII     0x02
+#define PHY_INTF_SEL_RMII      0x08
+
+struct s32_priv_data {
+       void __iomem *ioaddr;
+       void __iomem *ctrl_sts;
+       struct device *dev;
+       phy_interface_t *intf_mode;
+       struct clk *tx_clk;
+       struct clk *rx_clk;
+};
+
+static int s32_gmac_write_phy_intf_select(struct s32_priv_data *gmac)
+{
+       writel(PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
+
+       dev_dbg(gmac->dev, "PHY mode set to %s\n", phy_modes(*gmac->intf_mode));
+
+       return 0;
+}
+
+static int s32_gmac_init(struct platform_device *pdev, void *priv)
+{
+       struct s32_priv_data *gmac = priv;
+       int ret;
+
+       /* Set initial TX interface clock */
+       ret = clk_prepare_enable(gmac->tx_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't enable tx clock\n");
+               return ret;
+       }
+       ret = clk_set_rate(gmac->tx_clk, GMAC_INTF_RATE_125M);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set tx clock\n");
+               goto err_tx_disable;
+       }
+
+       /* Set initial RX interface clock */
+       ret = clk_prepare_enable(gmac->rx_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't enable rx clock\n");
+               goto err_tx_disable;
+       }
+       ret = clk_set_rate(gmac->rx_clk, GMAC_INTF_RATE_125M);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set rx clock\n");
+               goto err_txrx_disable;
+       }
+
+       /* Set interface mode */
+       ret = s32_gmac_write_phy_intf_select(gmac);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set PHY interface mode\n");
+               goto err_txrx_disable;
+       }
+
+       return 0;
+
+err_txrx_disable:
+       clk_disable_unprepare(gmac->rx_clk);
+err_tx_disable:
+       clk_disable_unprepare(gmac->tx_clk);
+       return ret;
+}
+
+static void s32_gmac_exit(struct platform_device *pdev, void *priv)
+{
+       struct s32_priv_data *gmac = priv;
+
+       clk_disable_unprepare(gmac->tx_clk);
+       clk_disable_unprepare(gmac->rx_clk);
+}
+
+static void s32_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+{
+       struct s32_priv_data *gmac = priv;
+       long tx_clk_rate;
+       int ret;
+
+       tx_clk_rate = rgmii_clock(speed);
+       if (tx_clk_rate < 0) {
+               dev_err(gmac->dev, "Unsupported/Invalid speed: %d\n", speed);
+               return;
+       }
+
+       dev_dbg(gmac->dev, "Set tx clock to %ld Hz\n", tx_clk_rate);
+       ret = clk_set_rate(gmac->tx_clk, tx_clk_rate);
+       if (ret)
+               dev_err(gmac->dev, "Can't set tx clock\n");
+}
+
+static int s32_dwmac_probe(struct platform_device *pdev)
+{
+       struct plat_stmmacenet_data *plat;
+       struct device *dev = &pdev->dev;
+       struct stmmac_resources res;
+       struct s32_priv_data *gmac;
+       int ret;
+
+       gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL);
+       if (!gmac)
+               return -ENOMEM;
+
+       gmac->dev = &pdev->dev;
+
+       ret = stmmac_get_platform_resources(pdev, &res);
+       if (ret)
+               return dev_err_probe(dev, ret,
+                                    "Failed to get platform resources\n");
+
+       plat = devm_stmmac_probe_config_dt(pdev, res.mac);
+       if (IS_ERR(plat))
+               return dev_err_probe(dev, PTR_ERR(plat),
+                                    "dt configuration failed\n");
+
+       /* PHY interface mode control reg */
+       gmac->ctrl_sts = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
+       if (IS_ERR(gmac->ctrl_sts))
+               return dev_err_probe(dev, PTR_ERR(gmac->ctrl_sts),
+                                    "S32CC config region is missing\n");
+
+       /* tx clock */
+       gmac->tx_clk = devm_clk_get(&pdev->dev, "tx");
+       if (IS_ERR(gmac->tx_clk))
+               return dev_err_probe(dev, PTR_ERR(gmac->tx_clk),
+                                    "tx clock not found\n");
+
+       /* rx clock */
+       gmac->rx_clk = devm_clk_get(&pdev->dev, "rx");
+       if (IS_ERR(gmac->rx_clk))
+               return dev_err_probe(dev, PTR_ERR(gmac->rx_clk),
+                                    "rx clock not found\n");
+
+       gmac->intf_mode = &plat->phy_interface;
+       gmac->ioaddr = res.addr;
+
+       /* S32CC core feature set */
+       plat->has_gmac4 = true;
+       plat->pmt = 1;
+       plat->flags |= STMMAC_FLAG_SPH_DISABLE;
+       plat->rx_fifo_size = 20480;
+       plat->tx_fifo_size = 20480;
+
+       plat->init = s32_gmac_init;
+       plat->exit = s32_gmac_exit;
+       plat->fix_mac_speed = s32_fix_mac_speed;
+
+       plat->bsp_priv = gmac;
+
+       return stmmac_pltfr_probe(pdev, plat, &res);
+}
+
+static const struct of_device_id s32_dwmac_match[] = {
+       { .compatible = "nxp,s32g2-dwmac" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, s32_dwmac_match);
+
+static struct platform_driver s32_dwmac_driver = {
+       .probe = s32_dwmac_probe,
+       .remove = stmmac_pltfr_remove,
+       .driver = {
+               .name = "s32-dwmac",
+               .pm = &stmmac_pltfr_pm_ops,
+               .of_match_table = s32_dwmac_match,
+       },
+};
+module_platform_driver(s32_dwmac_driver);
+
+MODULE_AUTHOR("Jan Petrous (OSS) <jan.petrous@oss.nxp.com>");
+MODULE_DESCRIPTION("NXP S32G/R common chassis GMAC driver");
+MODULE_LICENSE("GPL");
+