[PATCH 5/5] net: stmmac: Add NXP S32 SoC family support

From: Chester Lin
Date: Mon Oct 31 2022 - 06:12:36 EST


Add GMAC support for NXP S32 SoC family. This driver is mainly based on
NXP's downstream implementation on CodeAurora[1].

[1] https://source.codeaurora.org/external/autobsps32/linux/tree/drivers/net/ethernet/stmicro/stmmac?h=bsp34.0-5.10.120-rt

Signed-off-by: Jan Petrous <jan.petrous@xxxxxxx>
Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@xxxxxxx>
Signed-off-by: Andra-Teodora Ilie <andra.ilie@xxxxxxx>
Signed-off-by: Chester Lin <clin@xxxxxxxx>
---
drivers/net/ethernet/stmicro/stmmac/Kconfig | 13 +
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
.../net/ethernet/stmicro/stmmac/dwmac-s32cc.c | 318 ++++++++++++++++++
3 files changed, 332 insertions(+)
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-s32cc.c

diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 31ff35174034..dd3fb5e462b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -153,6 +153,19 @@ config DWMAC_ROCKCHIP
This selects the Rockchip RK3288 SoC glue layer support for
the stmmac device driver.

+config DWMAC_S32CC
+ tristate "NXP S32 series GMAC support"
+ default ARCH_S32
+ depends on OF && (ARCH_S32 || COMPILE_TEST)
+ select MFD_SYSCON
+ select PHYLINK
+ help
+ Support for ethernet controller on NXP S32 series SOCs.
+
+ This selects NXP SoC glue layer support for the stmmac
+ device driver. This driver is used for the S32 series
+ SOCs GMAC ethernet controller.
+
config DWMAC_SOCFPGA
tristate "SOCFPGA dwmac support"
default ARCH_INTEL_SOCFPGA
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index d4e12e9ace4f..ec92cc2becd7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o
obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o
obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o
obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o
+obj-$(CONFIG_DWMAC_S32CC) += dwmac-s32cc.o
stmmac-platform-objs:= stmmac_platform.o
dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o

diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-s32cc.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-s32cc.c
new file mode 100644
index 000000000000..ac274cdfbe22
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-s32cc.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DWMAC Specific Glue layer for NXP S32 Common Chassis
+ *
+ * Copyright (C) 2019-2022 NXP
+ * Copyright (C) 2022 SUSE LLC
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/ethtool.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/stmmac.h>
+
+#include "stmmac_platform.h"
+
+#define GMAC_TX_RATE_125M 125000000 /* 125MHz */
+#define GMAC_TX_RATE_25M 25000000 /* 25MHz */
+#define GMAC_TX_RATE_2M5 2500000 /* 2.5MHz */
+
+/* S32 SRC register for phyif selection */
+#define PHY_INTF_SEL_MII 0x00
+#define PHY_INTF_SEL_SGMII 0x01
+#define PHY_INTF_SEL_RGMII 0x02
+#define PHY_INTF_SEL_RMII 0x08
+
+/* AXI4 ACE control settings */
+#define ACE_DOMAIN_SIGNAL 0x2
+#define ACE_CACHE_SIGNAL 0xf
+#define ACE_CONTROL_SIGNALS ((ACE_DOMAIN_SIGNAL << 4) | ACE_CACHE_SIGNAL)
+#define ACE_PROTECTION 0x2
+
+struct s32cc_priv_data {
+ void __iomem *ctrl_sts;
+ struct device *dev;
+ phy_interface_t intf_mode;
+ struct clk *tx_clk;
+ struct clk *rx_clk;
+};
+
+static int s32cc_gmac_init(struct platform_device *pdev, void *priv)
+{
+ struct s32cc_priv_data *gmac = priv;
+ u32 intf_sel;
+ int ret;
+
+ if (gmac->tx_clk) {
+ ret = clk_prepare_enable(gmac->tx_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set tx clock\n");
+ return ret;
+ }
+ }
+
+ if (gmac->rx_clk) {
+ ret = clk_prepare_enable(gmac->rx_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't set rx clock\n");
+ return ret;
+ }
+ }
+
+ /* set interface mode */
+ if (gmac->ctrl_sts) {
+ switch (gmac->intf_mode) {
+ default:
+ dev_info(&pdev->dev, "unsupported mode %u, set the default phy mode.\n",
+ gmac->intf_mode);
+ fallthrough;
+ case PHY_INTERFACE_MODE_SGMII:
+ dev_info(&pdev->dev, "phy mode set to SGMII\n");
+ intf_sel = PHY_INTF_SEL_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ dev_info(&pdev->dev, "phy mode set to RGMII\n");
+ intf_sel = PHY_INTF_SEL_RGMII;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ dev_info(&pdev->dev, "phy mode set to RMII\n");
+ intf_sel = PHY_INTF_SEL_RMII;
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ dev_info(&pdev->dev, "phy mode set to MII\n");
+ intf_sel = PHY_INTF_SEL_MII;
+ break;
+ }
+
+ writel(intf_sel, gmac->ctrl_sts);
+ }
+
+ return 0;
+}
+
+static void s32cc_gmac_exit(struct platform_device *pdev, void *priv)
+{
+ struct s32cc_priv_data *gmac = priv;
+
+ if (gmac->tx_clk)
+ clk_disable_unprepare(gmac->tx_clk);
+
+ if (gmac->rx_clk)
+ clk_disable_unprepare(gmac->rx_clk);
+}
+
+static void s32cc_fix_speed(void *priv, unsigned int speed)
+{
+ struct s32cc_priv_data *gmac = priv;
+
+ if (!gmac->tx_clk || !gmac->rx_clk)
+ return;
+
+ /* SGMII mode doesn't support the clock reconfiguration */
+ if (gmac->intf_mode == PHY_INTERFACE_MODE_SGMII)
+ return;
+
+ switch (speed) {
+ case SPEED_1000:
+ dev_info(gmac->dev, "Set TX clock to 125M\n");
+ clk_set_rate(gmac->tx_clk, GMAC_TX_RATE_125M);
+ break;
+ case SPEED_100:
+ dev_info(gmac->dev, "Set TX clock to 25M\n");
+ clk_set_rate(gmac->tx_clk, GMAC_TX_RATE_25M);
+ break;
+ case SPEED_10:
+ dev_info(gmac->dev, "Set TX clock to 2.5M\n");
+ clk_set_rate(gmac->tx_clk, GMAC_TX_RATE_2M5);
+ break;
+ default:
+ dev_err(gmac->dev, "Unsupported/Invalid speed: %d\n", speed);
+ return;
+ }
+}
+
+static int s32cc_config_cache_coherency(struct platform_device *pdev,
+ struct plat_stmmacenet_data *plat_dat)
+{
+ plat_dat->axi4_ace_ctrl =
+ devm_kzalloc(&pdev->dev,
+ sizeof(struct stmmac_axi4_ace_ctrl),
+ GFP_KERNEL);
+
+ if (!plat_dat->axi4_ace_ctrl) {
+ dev_info(&pdev->dev, "Fail to allocate axi4_ace_ctrl\n");
+ return -ENOMEM;
+ }
+
+ plat_dat->axi4_ace_ctrl->tx_ar_reg = (ACE_CONTROL_SIGNALS << 16)
+ | (ACE_CONTROL_SIGNALS << 8) | ACE_CONTROL_SIGNALS;
+
+ plat_dat->axi4_ace_ctrl->rx_aw_reg = (ACE_CONTROL_SIGNALS << 24)
+ | (ACE_CONTROL_SIGNALS << 16) | (ACE_CONTROL_SIGNALS << 8)
+ | ACE_CONTROL_SIGNALS;
+
+ plat_dat->axi4_ace_ctrl->txrx_awar_reg = (ACE_PROTECTION << 20)
+ | (ACE_PROTECTION << 16) | (ACE_CONTROL_SIGNALS << 8)
+ | ACE_CONTROL_SIGNALS;
+
+ return 0;
+}
+
+static int s32cc_dwmac_probe(struct platform_device *pdev)
+{
+ struct plat_stmmacenet_data *plat_dat;
+ struct stmmac_resources stmmac_res;
+ struct s32cc_priv_data *gmac;
+ struct resource *res;
+ const char *tx_clk, *rx_clk;
+ int ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return ret;
+
+ gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL);
+ if (!gmac)
+ return PTR_ERR(gmac);
+
+ gmac->dev = &pdev->dev;
+
+ /* S32G control reg */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ gmac->ctrl_sts = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR_OR_NULL(gmac->ctrl_sts)) {
+ dev_err(&pdev->dev, "S32CC config region is missing\n");
+ return PTR_ERR(gmac->ctrl_sts);
+ }
+
+ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+ plat_dat->bsp_priv = gmac;
+
+ switch (plat_dat->phy_interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ tx_clk = "tx_sgmii";
+ rx_clk = "rx_sgmii";
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ tx_clk = "tx_rgmii";
+ rx_clk = "rx_rgmii";
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ tx_clk = "tx_rmii";
+ rx_clk = "rx_rmii";
+ break;
+ case PHY_INTERFACE_MODE_MII:
+ tx_clk = "tx_mii";
+ rx_clk = "rx_mii";
+ break;
+ default:
+ dev_err(&pdev->dev, "Not supported phy interface mode: [%s]\n",
+ phy_modes(plat_dat->phy_interface));
+ return -EINVAL;
+ };
+
+ gmac->intf_mode = plat_dat->phy_interface;
+
+ /* DMA cache coherency settings */
+ if (of_dma_is_coherent(pdev->dev.of_node)) {
+ ret = s32cc_config_cache_coherency(pdev, plat_dat);
+ if (ret)
+ goto err_remove_config_dt;
+ }
+
+ /* tx clock */
+ gmac->tx_clk = devm_clk_get(&pdev->dev, tx_clk);
+ if (IS_ERR(gmac->tx_clk)) {
+ dev_info(&pdev->dev, "tx clock not found\n");
+ gmac->tx_clk = NULL;
+ }
+
+ /* rx clock */
+ gmac->rx_clk = devm_clk_get(&pdev->dev, rx_clk);
+ if (IS_ERR(gmac->rx_clk)) {
+ dev_info(&pdev->dev, "rx clock not found\n");
+ gmac->rx_clk = NULL;
+ }
+
+ ret = s32cc_gmac_init(pdev, gmac);
+ if (ret)
+ goto err_remove_config_dt;
+
+ /* core feature set */
+ plat_dat->has_gmac4 = true;
+ plat_dat->pmt = 1;
+
+ plat_dat->init = s32cc_gmac_init;
+ plat_dat->exit = s32cc_gmac_exit;
+ plat_dat->fix_mac_speed = s32cc_fix_speed;
+
+ /* safety feature config */
+ plat_dat->safety_feat_cfg =
+ devm_kzalloc(&pdev->dev, sizeof(*plat_dat->safety_feat_cfg),
+ GFP_KERNEL);
+
+ if (!plat_dat->safety_feat_cfg) {
+ dev_info(&pdev->dev, "allocate safety_feat_cfg failed\n");
+ goto err_gmac_exit;
+ }
+
+ plat_dat->safety_feat_cfg->tsoee = 1;
+ plat_dat->safety_feat_cfg->mrxpee = 1;
+ plat_dat->safety_feat_cfg->mestee = 1;
+ plat_dat->safety_feat_cfg->mrxee = 1;
+ plat_dat->safety_feat_cfg->mtxee = 1;
+ plat_dat->safety_feat_cfg->epsi = 1;
+ plat_dat->safety_feat_cfg->edpp = 1;
+ plat_dat->safety_feat_cfg->prtyen = 1;
+ plat_dat->safety_feat_cfg->tmouten = 1;
+
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ if (ret)
+ goto err_gmac_exit;
+
+ return 0;
+
+err_gmac_exit:
+ s32cc_gmac_exit(pdev, plat_dat->bsp_priv);
+err_remove_config_dt:
+ stmmac_remove_config_dt(pdev, plat_dat);
+ return ret;
+}
+
+static const struct of_device_id s32_dwmac_match[] = {
+ { .compatible = "nxp,s32cc-dwmac" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s32_dwmac_match);
+
+static struct platform_driver s32_dwmac_driver = {
+ .probe = s32cc_dwmac_probe,
+ .remove = stmmac_pltfr_remove,
+ .driver = {
+ .name = "s32cc-dwmac",
+ .pm = &stmmac_pltfr_pm_ops,
+ .of_match_table = s32_dwmac_match,
+ },
+};
+module_platform_driver(s32_dwmac_driver);
+
+MODULE_AUTHOR("Jan Petrous <jan.petrous@xxxxxxx>");
+MODULE_DESCRIPTION("NXP S32 common chassis GMAC driver");
+MODULE_LICENSE("GPL v2");
--
2.37.3