1 // SPDX-License-Identifier: GPL-2.0
3 * dwmac-imx.c - DWMAC Specific Glue layer for NXP imx8
10 #include <linux/gpio/consumer.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.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>
24 #include "stmmac_platform.h"
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)
34 struct imx_dwmac_ops
{
36 bool mac_rgmii_txclk_auto_adj
;
38 int (*set_intf_mode
)(struct plat_stmmacenet_data
*plat_dat
);
41 struct imx_priv_data
{
45 struct regmap
*intf_regmap
;
49 const struct imx_dwmac_ops
*ops
;
50 struct plat_stmmacenet_data
*plat_dat
;
53 static int imx8mp_set_intf_mode(struct plat_stmmacenet_data
*plat_dat
)
55 struct imx_priv_data
*dwmac
= plat_dat
->bsp_priv
;
58 switch (plat_dat
->interface
) {
59 case PHY_INTERFACE_MODE_MII
:
60 val
= GPR_ENET_QOS_INTF_SEL_MII
;
62 case PHY_INTERFACE_MODE_RMII
:
63 val
= GPR_ENET_QOS_INTF_SEL_RMII
;
64 val
|= (dwmac
->rmii_refclk_ext
? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL
);
66 case PHY_INTERFACE_MODE_RGMII
:
67 case PHY_INTERFACE_MODE_RGMII_ID
:
68 case PHY_INTERFACE_MODE_RGMII_RXID
:
69 case PHY_INTERFACE_MODE_RGMII_TXID
:
70 val
= GPR_ENET_QOS_INTF_SEL_RGMII
|
71 GPR_ENET_QOS_RGMII_EN
;
74 pr_debug("imx dwmac doesn't support %d interface\n",
79 val
|= GPR_ENET_QOS_CLK_GEN_EN
;
80 return regmap_update_bits(dwmac
->intf_regmap
, dwmac
->intf_reg_off
,
81 GPR_ENET_QOS_INTF_MODE_MASK
, val
);
85 imx8dxl_set_intf_mode(struct plat_stmmacenet_data
*plat_dat
)
89 /* TBD: depends on imx8dxl scu interfaces to be upstreamed */
93 static int imx_dwmac_init(struct platform_device
*pdev
, void *priv
)
95 struct plat_stmmacenet_data
*plat_dat
;
96 struct imx_priv_data
*dwmac
= priv
;
99 plat_dat
= dwmac
->plat_dat
;
101 ret
= clk_prepare_enable(dwmac
->clk_mem
);
103 dev_err(&pdev
->dev
, "mem clock enable failed\n");
107 ret
= clk_prepare_enable(dwmac
->clk_tx
);
109 dev_err(&pdev
->dev
, "tx clock enable failed\n");
110 goto clk_tx_en_failed
;
113 if (dwmac
->ops
->set_intf_mode
) {
114 ret
= dwmac
->ops
->set_intf_mode(plat_dat
);
116 goto intf_mode_failed
;
122 clk_disable_unprepare(dwmac
->clk_tx
);
124 clk_disable_unprepare(dwmac
->clk_mem
);
128 static void imx_dwmac_exit(struct platform_device
*pdev
, void *priv
)
130 struct imx_priv_data
*dwmac
= priv
;
132 clk_disable_unprepare(dwmac
->clk_tx
);
133 clk_disable_unprepare(dwmac
->clk_mem
);
136 static void imx_dwmac_fix_speed(void *priv
, unsigned int speed
)
138 struct plat_stmmacenet_data
*plat_dat
;
139 struct imx_priv_data
*dwmac
= priv
;
143 plat_dat
= dwmac
->plat_dat
;
145 if (dwmac
->ops
->mac_rgmii_txclk_auto_adj
||
146 (plat_dat
->interface
== PHY_INTERFACE_MODE_RMII
) ||
147 (plat_dat
->interface
== PHY_INTERFACE_MODE_MII
))
161 dev_err(dwmac
->dev
, "invalid speed %u\n", speed
);
165 err
= clk_set_rate(dwmac
->clk_tx
, rate
);
167 dev_err(dwmac
->dev
, "failed to set tx rate %lu\n", rate
);
171 imx_dwmac_parse_dt(struct imx_priv_data
*dwmac
, struct device
*dev
)
173 struct device_node
*np
= dev
->of_node
;
176 if (of_get_property(np
, "snps,rmii_refclk_ext", NULL
))
177 dwmac
->rmii_refclk_ext
= true;
179 dwmac
->clk_tx
= devm_clk_get(dev
, "tx");
180 if (IS_ERR(dwmac
->clk_tx
)) {
181 dev_err(dev
, "failed to get tx clock\n");
182 return PTR_ERR(dwmac
->clk_tx
);
185 dwmac
->clk_mem
= NULL
;
186 if (of_machine_is_compatible("fsl,imx8dxl")) {
187 dwmac
->clk_mem
= devm_clk_get(dev
, "mem");
188 if (IS_ERR(dwmac
->clk_mem
)) {
189 dev_err(dev
, "failed to get mem clock\n");
190 return PTR_ERR(dwmac
->clk_mem
);
194 if (of_machine_is_compatible("fsl,imx8mp")) {
195 /* Binding doc describes the propety:
196 is required by i.MX8MP.
197 is optinoal for i.MX8DXL.
199 dwmac
->intf_regmap
= syscon_regmap_lookup_by_phandle(np
, "intf_mode");
200 if (IS_ERR(dwmac
->intf_regmap
))
201 return PTR_ERR(dwmac
->intf_regmap
);
203 err
= of_property_read_u32_index(np
, "intf_mode", 1, &dwmac
->intf_reg_off
);
205 dev_err(dev
, "Can't get intf mode reg offset (%d)\n", err
);
213 static int imx_dwmac_probe(struct platform_device
*pdev
)
215 struct plat_stmmacenet_data
*plat_dat
;
216 struct stmmac_resources stmmac_res
;
217 struct imx_priv_data
*dwmac
;
218 const struct imx_dwmac_ops
*data
;
221 ret
= stmmac_get_platform_resources(pdev
, &stmmac_res
);
225 dwmac
= devm_kzalloc(&pdev
->dev
, sizeof(*dwmac
), GFP_KERNEL
);
229 plat_dat
= stmmac_probe_config_dt(pdev
, &stmmac_res
.mac
);
230 if (IS_ERR(plat_dat
))
231 return PTR_ERR(plat_dat
);
233 data
= of_device_get_match_data(&pdev
->dev
);
235 dev_err(&pdev
->dev
, "failed to get match data\n");
241 dwmac
->dev
= &pdev
->dev
;
243 ret
= imx_dwmac_parse_dt(dwmac
, &pdev
->dev
);
245 dev_err(&pdev
->dev
, "failed to parse OF data\n");
249 plat_dat
->addr64
= dwmac
->ops
->addr_width
;
250 plat_dat
->init
= imx_dwmac_init
;
251 plat_dat
->exit
= imx_dwmac_exit
;
252 plat_dat
->fix_mac_speed
= imx_dwmac_fix_speed
;
253 plat_dat
->bsp_priv
= dwmac
;
254 dwmac
->plat_dat
= plat_dat
;
256 ret
= imx_dwmac_init(pdev
, dwmac
);
260 ret
= stmmac_dvr_probe(&pdev
->dev
, plat_dat
, &stmmac_res
);
268 imx_dwmac_exit(pdev
, plat_dat
->bsp_priv
);
271 stmmac_remove_config_dt(pdev
, plat_dat
);
275 static struct imx_dwmac_ops imx8mp_dwmac_data
= {
277 .mac_rgmii_txclk_auto_adj
= false,
278 .set_intf_mode
= imx8mp_set_intf_mode
,
281 static struct imx_dwmac_ops imx8dxl_dwmac_data
= {
283 .mac_rgmii_txclk_auto_adj
= true,
284 .set_intf_mode
= imx8dxl_set_intf_mode
,
287 static const struct of_device_id imx_dwmac_match
[] = {
288 { .compatible
= "nxp,imx8mp-dwmac-eqos", .data
= &imx8mp_dwmac_data
},
289 { .compatible
= "nxp,imx8dxl-dwmac-eqos", .data
= &imx8dxl_dwmac_data
},
292 MODULE_DEVICE_TABLE(of
, imx_dwmac_match
);
294 static struct platform_driver imx_dwmac_driver
= {
295 .probe
= imx_dwmac_probe
,
296 .remove
= stmmac_pltfr_remove
,
299 .pm
= &stmmac_pltfr_pm_ops
,
300 .of_match_table
= imx_dwmac_match
,
303 module_platform_driver(imx_dwmac_driver
);
305 MODULE_AUTHOR("NXP");
306 MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer");
307 MODULE_LICENSE("GPL v2");