xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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