1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * NXP S32G/R GMAC glue layer
4 *
5 * Copyright 2019-2026 NXP
6 *
7 */
8
9 #include <linux/clk.h>
10 #include <linux/clk-provider.h>
11 #include <linux/device.h>
12 #include <linux/ethtool.h>
13 #include <linux/io.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/module.h>
16 #include <linux/of_mdio.h>
17 #include <linux/of_address.h>
18 #include <linux/phy.h>
19 #include <linux/phylink.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 #include <linux/stmmac.h>
23
24 #include "stmmac_platform.h"
25
26 #define GMAC_INTF_RATE_125M 125000000 /* 125MHz */
27
28 /* SoC PHY interface control register */
29 #define S32_PHY_INTF_SEL_MII 0x00
30 #define S32_PHY_INTF_SEL_SGMII 0x01
31 #define S32_PHY_INTF_SEL_RGMII 0x02
32 #define S32_PHY_INTF_SEL_RMII 0x08
33
34 struct s32_priv_data {
35 void __iomem *ioaddr;
36 void __iomem *ctrl_sts;
37 struct regmap *sts_regmap;
38 unsigned int sts_offset;
39 struct device *dev;
40 phy_interface_t *intf_mode;
41 struct clk *tx_clk;
42 struct clk *rx_clk;
43 };
44
s32_gmac_write_phy_intf_select(struct s32_priv_data * gmac)45 static int s32_gmac_write_phy_intf_select(struct s32_priv_data *gmac)
46 {
47 int ret = 0;
48
49 if (gmac->ctrl_sts)
50 writel(S32_PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
51 else
52 ret = regmap_write(gmac->sts_regmap, gmac->sts_offset,
53 S32_PHY_INTF_SEL_RGMII);
54
55 dev_dbg(gmac->dev, "PHY mode set to %s\n", phy_modes(*gmac->intf_mode));
56
57 return ret;
58 }
59
s32_gmac_init(struct device * dev,void * priv)60 static int s32_gmac_init(struct device *dev, void *priv)
61 {
62 struct s32_priv_data *gmac = priv;
63 int ret;
64
65 /* Set initial TX interface clock */
66 ret = clk_prepare_enable(gmac->tx_clk);
67 if (ret) {
68 dev_err(dev, "Can't enable tx clock\n");
69 return ret;
70 }
71 ret = clk_set_rate(gmac->tx_clk, GMAC_INTF_RATE_125M);
72 if (ret) {
73 dev_err(dev, "Can't set tx clock\n");
74 goto err_tx_disable;
75 }
76
77 /* Set initial RX interface clock */
78 ret = clk_prepare_enable(gmac->rx_clk);
79 if (ret) {
80 dev_err(dev, "Can't enable rx clock\n");
81 goto err_tx_disable;
82 }
83 ret = clk_set_rate(gmac->rx_clk, GMAC_INTF_RATE_125M);
84 if (ret) {
85 dev_err(dev, "Can't set rx clock\n");
86 goto err_txrx_disable;
87 }
88
89 /* Set interface mode */
90 ret = s32_gmac_write_phy_intf_select(gmac);
91 if (ret) {
92 dev_err(dev, "Can't set PHY interface mode\n");
93 goto err_txrx_disable;
94 }
95
96 return 0;
97
98 err_txrx_disable:
99 clk_disable_unprepare(gmac->rx_clk);
100 err_tx_disable:
101 clk_disable_unprepare(gmac->tx_clk);
102 return ret;
103 }
104
s32_gmac_exit(struct device * dev,void * priv)105 static void s32_gmac_exit(struct device *dev, void *priv)
106 {
107 struct s32_priv_data *gmac = priv;
108
109 clk_disable_unprepare(gmac->tx_clk);
110 clk_disable_unprepare(gmac->rx_clk);
111 }
112
s32_gmac_setup_multi_irq(struct device * dev,struct plat_stmmacenet_data * plat,struct stmmac_resources * res)113 static void s32_gmac_setup_multi_irq(struct device *dev,
114 struct plat_stmmacenet_data *plat,
115 struct stmmac_resources *res)
116 {
117 int i;
118
119 /* RX IRQs */
120 for (i = 0; i < plat->rx_queues_to_use; i++) {
121 if (res->rx_irq[i] <= 0) {
122 dev_dbg(dev, "Missing RX queue %d interrupt\n", i);
123 goto mac_irq_mode;
124 }
125 }
126
127 /* TX IRQs */
128 for (i = 0; i < plat->tx_queues_to_use; i++) {
129 if (res->tx_irq[i] <= 0) {
130 dev_dbg(dev, "Missing TX queue %d interrupt\n", i);
131 goto mac_irq_mode;
132 }
133 }
134
135 plat->flags |= STMMAC_FLAG_MULTI_MSI_EN;
136 dev_info(dev, "Multi-IRQ mode (per queue IRQs) selected\n");
137 return;
138
139 mac_irq_mode:
140 plat->flags &= ~STMMAC_FLAG_MULTI_MSI_EN;
141 dev_info(dev, "MAC IRQ mode selected\n");
142 }
143
s32_dwmac_probe(struct platform_device * pdev)144 static int s32_dwmac_probe(struct platform_device *pdev)
145 {
146 struct plat_stmmacenet_data *plat;
147 struct device *dev = &pdev->dev;
148 struct stmmac_resources res;
149 struct s32_priv_data *gmac;
150 int ret;
151
152 gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL);
153 if (!gmac)
154 return -ENOMEM;
155
156 gmac->dev = &pdev->dev;
157
158 ret = stmmac_get_platform_resources(pdev, &res);
159 if (ret)
160 return dev_err_probe(dev, ret,
161 "Failed to get platform resources\n");
162
163 plat = devm_stmmac_probe_config_dt(pdev, res.mac);
164 if (IS_ERR(plat))
165 return dev_err_probe(dev, PTR_ERR(plat),
166 "dt configuration failed\n");
167
168 /* PHY interface mode control reg */
169 gmac->sts_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
170 "nxp,phy-sel", 1, &gmac->sts_offset);
171 if (gmac->sts_regmap == ERR_PTR(-EPROBE_DEFER))
172 return PTR_ERR(gmac->sts_regmap);
173 if (IS_ERR(gmac->sts_regmap)) {
174 gmac->ctrl_sts = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
175 if (IS_ERR(gmac->ctrl_sts))
176 return dev_err_probe(dev, PTR_ERR(gmac->ctrl_sts),
177 "S32CC config region is missing\n");
178 }
179
180 /* tx clock */
181 gmac->tx_clk = devm_clk_get(&pdev->dev, "tx");
182 if (IS_ERR(gmac->tx_clk))
183 return dev_err_probe(dev, PTR_ERR(gmac->tx_clk),
184 "tx clock not found\n");
185
186 /* rx clock */
187 gmac->rx_clk = devm_clk_get(&pdev->dev, "rx");
188 if (IS_ERR(gmac->rx_clk))
189 return dev_err_probe(dev, PTR_ERR(gmac->rx_clk),
190 "rx clock not found\n");
191
192 gmac->intf_mode = &plat->phy_interface;
193 gmac->ioaddr = res.addr;
194
195 /* S32CC core feature set */
196 plat->core_type = DWMAC_CORE_GMAC4;
197 plat->pmt = true;
198 plat->flags |= STMMAC_FLAG_SPH_DISABLE;
199
200 s32_gmac_setup_multi_irq(dev, plat, &res);
201
202 plat->rx_fifo_size = 20480;
203 plat->tx_fifo_size = 20480;
204
205 plat->init = s32_gmac_init;
206 plat->exit = s32_gmac_exit;
207
208 plat->clk_tx_i = gmac->tx_clk;
209 plat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
210
211 plat->bsp_priv = gmac;
212
213 return stmmac_pltfr_probe(pdev, plat, &res);
214 }
215
216 static const struct of_device_id s32_dwmac_match[] = {
217 { .compatible = "nxp,s32g2-dwmac" },
218 { }
219 };
220 MODULE_DEVICE_TABLE(of, s32_dwmac_match);
221
222 static struct platform_driver s32_dwmac_driver = {
223 .probe = s32_dwmac_probe,
224 .remove = stmmac_pltfr_remove,
225 .driver = {
226 .name = "s32-dwmac",
227 .pm = &stmmac_pltfr_pm_ops,
228 .of_match_table = s32_dwmac_match,
229 },
230 };
231 module_platform_driver(s32_dwmac_driver);
232
233 MODULE_AUTHOR("Jan Petrous (OSS) <jan.petrous@oss.nxp.com>");
234 MODULE_DESCRIPTION("NXP S32G/R common chassis GMAC driver");
235 MODULE_LICENSE("GPL");
236
237