1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * dwmac-imx.c - DWMAC Specific Glue layer for NXP imx8
4 *
5 * Copyright 2020 NXP
6 *
7 */
8
9 #include <linux/clk.h>
10 #include <linux/gpio/consumer.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_device.h>
16 #include <linux/of_net.h>
17 #include <linux/phy.h>
18 #include <linux/platform_device.h>
19 #include <linux/pm_wakeirq.h>
20 #include <linux/regmap.h>
21 #include <linux/slab.h>
22 #include <linux/stmmac.h>
23
24 #include "stmmac_platform.h"
25
26 #define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16)
27 #define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16)
28 #define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16)
29 #define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16)
30 #define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19)
31 #define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20)
32 #define GPR_ENET_QOS_RGMII_EN (0x1 << 21)
33
34 #define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0)
35 #define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1)
36 #define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1)
37 #define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1)
38 #define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0)
39
40 struct imx_dwmac_ops {
41 u32 addr_width;
42 bool mac_rgmii_txclk_auto_adj;
43
44 int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
45 };
46
47 struct imx_priv_data {
48 struct device *dev;
49 struct clk *clk_tx;
50 struct clk *clk_mem;
51 struct regmap *intf_regmap;
52 u32 intf_reg_off;
53 bool rmii_refclk_ext;
54
55 const struct imx_dwmac_ops *ops;
56 struct plat_stmmacenet_data *plat_dat;
57 };
58
imx8mp_set_intf_mode(struct plat_stmmacenet_data * plat_dat)59 static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
60 {
61 struct imx_priv_data *dwmac = plat_dat->bsp_priv;
62 int val;
63
64 switch (plat_dat->interface) {
65 case PHY_INTERFACE_MODE_MII:
66 val = GPR_ENET_QOS_INTF_SEL_MII;
67 break;
68 case PHY_INTERFACE_MODE_RMII:
69 val = GPR_ENET_QOS_INTF_SEL_RMII;
70 val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
71 break;
72 case PHY_INTERFACE_MODE_RGMII:
73 case PHY_INTERFACE_MODE_RGMII_ID:
74 case PHY_INTERFACE_MODE_RGMII_RXID:
75 case PHY_INTERFACE_MODE_RGMII_TXID:
76 val = GPR_ENET_QOS_INTF_SEL_RGMII |
77 GPR_ENET_QOS_RGMII_EN;
78 break;
79 default:
80 pr_debug("imx dwmac doesn't support %d interface\n",
81 plat_dat->interface);
82 return -EINVAL;
83 }
84
85 val |= GPR_ENET_QOS_CLK_GEN_EN;
86 return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
87 GPR_ENET_QOS_INTF_MODE_MASK, val);
88 };
89
90 static int
imx8dxl_set_intf_mode(struct plat_stmmacenet_data * plat_dat)91 imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
92 {
93 int ret = 0;
94
95 /* TBD: depends on imx8dxl scu interfaces to be upstreamed */
96 return ret;
97 }
98
imx93_set_intf_mode(struct plat_stmmacenet_data * plat_dat)99 static int imx93_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
100 {
101 struct imx_priv_data *dwmac = plat_dat->bsp_priv;
102 int val;
103
104 switch (plat_dat->interface) {
105 case PHY_INTERFACE_MODE_MII:
106 val = MX93_GPR_ENET_QOS_INTF_SEL_MII;
107 break;
108 case PHY_INTERFACE_MODE_RMII:
109 val = MX93_GPR_ENET_QOS_INTF_SEL_RMII;
110 break;
111 case PHY_INTERFACE_MODE_RGMII:
112 case PHY_INTERFACE_MODE_RGMII_ID:
113 case PHY_INTERFACE_MODE_RGMII_RXID:
114 case PHY_INTERFACE_MODE_RGMII_TXID:
115 val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII;
116 break;
117 default:
118 dev_dbg(dwmac->dev, "imx dwmac doesn't support %d interface\n",
119 plat_dat->interface);
120 return -EINVAL;
121 }
122
123 val |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
124 return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
125 MX93_GPR_ENET_QOS_INTF_MODE_MASK, val);
126 };
127
imx_dwmac_clks_config(void * priv,bool enabled)128 static int imx_dwmac_clks_config(void *priv, bool enabled)
129 {
130 struct imx_priv_data *dwmac = priv;
131 int ret = 0;
132
133 if (enabled) {
134 ret = clk_prepare_enable(dwmac->clk_mem);
135 if (ret) {
136 dev_err(dwmac->dev, "mem clock enable failed\n");
137 return ret;
138 }
139
140 ret = clk_prepare_enable(dwmac->clk_tx);
141 if (ret) {
142 dev_err(dwmac->dev, "tx clock enable failed\n");
143 clk_disable_unprepare(dwmac->clk_mem);
144 return ret;
145 }
146 } else {
147 clk_disable_unprepare(dwmac->clk_tx);
148 clk_disable_unprepare(dwmac->clk_mem);
149 }
150
151 return ret;
152 }
153
imx_dwmac_init(struct platform_device * pdev,void * priv)154 static int imx_dwmac_init(struct platform_device *pdev, void *priv)
155 {
156 struct plat_stmmacenet_data *plat_dat;
157 struct imx_priv_data *dwmac = priv;
158 int ret;
159
160 plat_dat = dwmac->plat_dat;
161
162 if (dwmac->ops->set_intf_mode) {
163 ret = dwmac->ops->set_intf_mode(plat_dat);
164 if (ret)
165 return ret;
166 }
167
168 return 0;
169 }
170
imx_dwmac_exit(struct platform_device * pdev,void * priv)171 static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
172 {
173 /* nothing to do now */
174 }
175
imx_dwmac_fix_speed(void * priv,unsigned int speed)176 static void imx_dwmac_fix_speed(void *priv, unsigned int speed)
177 {
178 struct plat_stmmacenet_data *plat_dat;
179 struct imx_priv_data *dwmac = priv;
180 unsigned long rate;
181 int err;
182
183 plat_dat = dwmac->plat_dat;
184
185 if (dwmac->ops->mac_rgmii_txclk_auto_adj ||
186 (plat_dat->interface == PHY_INTERFACE_MODE_RMII) ||
187 (plat_dat->interface == PHY_INTERFACE_MODE_MII))
188 return;
189
190 switch (speed) {
191 case SPEED_1000:
192 rate = 125000000;
193 break;
194 case SPEED_100:
195 rate = 25000000;
196 break;
197 case SPEED_10:
198 rate = 2500000;
199 break;
200 default:
201 dev_err(dwmac->dev, "invalid speed %u\n", speed);
202 return;
203 }
204
205 err = clk_set_rate(dwmac->clk_tx, rate);
206 if (err < 0)
207 dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
208 }
209
210 static int
imx_dwmac_parse_dt(struct imx_priv_data * dwmac,struct device * dev)211 imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev)
212 {
213 struct device_node *np = dev->of_node;
214 int err = 0;
215
216 if (of_get_property(np, "snps,rmii_refclk_ext", NULL))
217 dwmac->rmii_refclk_ext = true;
218
219 dwmac->clk_tx = devm_clk_get(dev, "tx");
220 if (IS_ERR(dwmac->clk_tx)) {
221 dev_err(dev, "failed to get tx clock\n");
222 return PTR_ERR(dwmac->clk_tx);
223 }
224
225 dwmac->clk_mem = NULL;
226
227 if (of_machine_is_compatible("fsl,imx8dxl") ||
228 of_machine_is_compatible("fsl,imx93")) {
229 dwmac->clk_mem = devm_clk_get(dev, "mem");
230 if (IS_ERR(dwmac->clk_mem)) {
231 dev_err(dev, "failed to get mem clock\n");
232 return PTR_ERR(dwmac->clk_mem);
233 }
234 }
235
236 if (of_machine_is_compatible("fsl,imx8mp") ||
237 of_machine_is_compatible("fsl,imx93")) {
238 /* Binding doc describes the propety:
239 * is required by i.MX8MP, i.MX93.
240 * is optinoal for i.MX8DXL.
241 */
242 dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode");
243 if (IS_ERR(dwmac->intf_regmap))
244 return PTR_ERR(dwmac->intf_regmap);
245
246 err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off);
247 if (err) {
248 dev_err(dev, "Can't get intf mode reg offset (%d)\n", err);
249 return err;
250 }
251 }
252
253 return err;
254 }
255
imx_dwmac_probe(struct platform_device * pdev)256 static int imx_dwmac_probe(struct platform_device *pdev)
257 {
258 struct plat_stmmacenet_data *plat_dat;
259 struct stmmac_resources stmmac_res;
260 struct imx_priv_data *dwmac;
261 const struct imx_dwmac_ops *data;
262 int ret;
263
264 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
265 if (ret)
266 return ret;
267
268 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
269 if (!dwmac)
270 return -ENOMEM;
271
272 plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
273 if (IS_ERR(plat_dat))
274 return PTR_ERR(plat_dat);
275
276 data = of_device_get_match_data(&pdev->dev);
277 if (!data) {
278 dev_err(&pdev->dev, "failed to get match data\n");
279 ret = -EINVAL;
280 goto err_match_data;
281 }
282
283 dwmac->ops = data;
284 dwmac->dev = &pdev->dev;
285
286 ret = imx_dwmac_parse_dt(dwmac, &pdev->dev);
287 if (ret) {
288 dev_err(&pdev->dev, "failed to parse OF data\n");
289 goto err_parse_dt;
290 }
291
292 plat_dat->addr64 = dwmac->ops->addr_width;
293 plat_dat->init = imx_dwmac_init;
294 plat_dat->exit = imx_dwmac_exit;
295 plat_dat->clks_config = imx_dwmac_clks_config;
296 plat_dat->fix_mac_speed = imx_dwmac_fix_speed;
297 plat_dat->bsp_priv = dwmac;
298 dwmac->plat_dat = plat_dat;
299
300 ret = imx_dwmac_clks_config(dwmac, true);
301 if (ret)
302 goto err_clks_config;
303
304 ret = imx_dwmac_init(pdev, dwmac);
305 if (ret)
306 goto err_dwmac_init;
307
308 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
309 if (ret)
310 goto err_drv_probe;
311
312 return 0;
313
314 err_drv_probe:
315 imx_dwmac_exit(pdev, plat_dat->bsp_priv);
316 err_dwmac_init:
317 imx_dwmac_clks_config(dwmac, false);
318 err_clks_config:
319 err_parse_dt:
320 err_match_data:
321 stmmac_remove_config_dt(pdev, plat_dat);
322 return ret;
323 }
324
325 static struct imx_dwmac_ops imx8mp_dwmac_data = {
326 .addr_width = 34,
327 .mac_rgmii_txclk_auto_adj = false,
328 .set_intf_mode = imx8mp_set_intf_mode,
329 };
330
331 static struct imx_dwmac_ops imx8dxl_dwmac_data = {
332 .addr_width = 32,
333 .mac_rgmii_txclk_auto_adj = true,
334 .set_intf_mode = imx8dxl_set_intf_mode,
335 };
336
337 static struct imx_dwmac_ops imx93_dwmac_data = {
338 .addr_width = 32,
339 .mac_rgmii_txclk_auto_adj = true,
340 .set_intf_mode = imx93_set_intf_mode,
341 };
342
343 static const struct of_device_id imx_dwmac_match[] = {
344 { .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data },
345 { .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data },
346 { .compatible = "nxp,imx93-dwmac-eqos", .data = &imx93_dwmac_data },
347 { }
348 };
349 MODULE_DEVICE_TABLE(of, imx_dwmac_match);
350
351 static struct platform_driver imx_dwmac_driver = {
352 .probe = imx_dwmac_probe,
353 .remove = stmmac_pltfr_remove,
354 .driver = {
355 .name = "imx-dwmac",
356 .pm = &stmmac_pltfr_pm_ops,
357 .of_match_table = imx_dwmac_match,
358 },
359 };
360 module_platform_driver(imx_dwmac_driver);
361
362 MODULE_AUTHOR("NXP");
363 MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer");
364 MODULE_LICENSE("GPL v2");
365