xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c (revision 68993ced0f618e36cf33388f1e50223e5e6e78cc)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Eswin DWC Ethernet linux driver
4  *
5  * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd.
6  *
7  * Authors:
8  *   Zhi Li <lizhi2@eswincomputing.com>
9  *   Shuang Liang <liangshuang@eswincomputing.com>
10  *   Shangjuan Wei <weishangjuan@eswincomputing.com>
11  */
12 
13 #include <linux/platform_device.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/pm_runtime.h>
16 #include <linux/stmmac.h>
17 #include <linux/regmap.h>
18 #include <linux/of.h>
19 
20 #include "stmmac_platform.h"
21 
22 /* eth_phy_ctrl_offset eth0:0x100 */
23 #define EIC7700_ETH_TX_CLK_SEL		BIT(16)
24 #define EIC7700_ETH_PHY_INTF_SELI	BIT(0)
25 
26 /* eth_axi_lp_ctrl_offset eth0:0x108 */
27 #define EIC7700_ETH_CSYSREQ_VAL		BIT(0)
28 
29 /*
30  * TX/RX Clock Delay Bit Masks:
31  * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.02ns per bit)
32  * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.02ns per bit)
33  */
34 #define EIC7700_ETH_TX_ADJ_DELAY	GENMASK(14, 8)
35 #define EIC7700_ETH_RX_ADJ_DELAY	GENMASK(30, 24)
36 
37 #define EIC7700_MAX_DELAY_STEPS		0x7F
38 #define EIC7700_DELAY_STEP_PS		20
39 #define EIC7700_MAX_DELAY_PS	\
40 	(EIC7700_MAX_DELAY_STEPS * EIC7700_DELAY_STEP_PS)
41 
42 static const char * const eic7700_clk_names[] = {
43 	"tx", "axi", "cfg",
44 };
45 
46 struct eic7700_qos_priv {
47 	struct plat_stmmacenet_data *plat_dat;
48 	struct regmap *eic7700_hsp_regmap;
49 	u32 eth_axi_lp_ctrl_offset;
50 	u32 eth_phy_ctrl_offset;
51 	u32 eth_clk_offset;
52 	u32 eth_txd_offset;
53 	u32 eth_rxd_offset;
54 	u32 eth_clk_dly_param;
55 	bool has_txd_offset;
56 	bool has_rxd_offset;
57 };
58 
eic7700_clks_config(void * priv,bool enabled)59 static int eic7700_clks_config(void *priv, bool enabled)
60 {
61 	struct eic7700_qos_priv *dwc = (struct eic7700_qos_priv *)priv;
62 	struct plat_stmmacenet_data *plat = dwc->plat_dat;
63 	int ret = 0;
64 
65 	if (enabled)
66 		ret = clk_bulk_prepare_enable(plat->num_clks, plat->clks);
67 	else
68 		clk_bulk_disable_unprepare(plat->num_clks, plat->clks);
69 
70 	return ret;
71 }
72 
eic7700_dwmac_init(struct device * dev,void * priv)73 static int eic7700_dwmac_init(struct device *dev, void *priv)
74 {
75 	struct eic7700_qos_priv *dwc = priv;
76 	int ret;
77 
78 	ret = eic7700_clks_config(dwc, true);
79 	if (ret)
80 		return ret;
81 
82 	ret = regmap_set_bits(dwc->eic7700_hsp_regmap,
83 			      dwc->eth_phy_ctrl_offset,
84 			      EIC7700_ETH_TX_CLK_SEL |
85 			      EIC7700_ETH_PHY_INTF_SELI);
86 	if (ret) {
87 		eic7700_clks_config(dwc, false);
88 		return ret;
89 	}
90 
91 	regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_axi_lp_ctrl_offset,
92 		     EIC7700_ETH_CSYSREQ_VAL);
93 
94 	if (dwc->has_txd_offset)
95 		regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_txd_offset, 0);
96 
97 	if (dwc->has_rxd_offset)
98 		regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_rxd_offset, 0);
99 
100 	regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_clk_offset,
101 		     dwc->eth_clk_dly_param);
102 
103 	return 0;
104 }
105 
eic7700_dwmac_exit(struct device * dev,void * priv)106 static void eic7700_dwmac_exit(struct device *dev, void *priv)
107 {
108 	struct eic7700_qos_priv *dwc = priv;
109 
110 	eic7700_clks_config(dwc, false);
111 }
112 
eic7700_dwmac_suspend(struct device * dev,void * priv)113 static int eic7700_dwmac_suspend(struct device *dev, void *priv)
114 {
115 	return pm_runtime_force_suspend(dev);
116 }
117 
eic7700_dwmac_resume(struct device * dev,void * priv)118 static int eic7700_dwmac_resume(struct device *dev, void *priv)
119 {
120 	int ret;
121 
122 	ret = pm_runtime_force_resume(dev);
123 	if (ret)
124 		dev_err(dev, "%s failed: %d\n", __func__, ret);
125 
126 	return ret;
127 }
128 
eic7700_dwmac_probe(struct platform_device * pdev)129 static int eic7700_dwmac_probe(struct platform_device *pdev)
130 {
131 	struct plat_stmmacenet_data *plat_dat;
132 	struct stmmac_resources stmmac_res;
133 	struct eic7700_qos_priv *dwc_priv;
134 	u32 delay_ps, val;
135 	int i, ret;
136 
137 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
138 	if (ret)
139 		return dev_err_probe(&pdev->dev, ret,
140 				"failed to get resources\n");
141 
142 	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
143 	if (IS_ERR(plat_dat))
144 		return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat),
145 				"dt configuration failed\n");
146 
147 	dwc_priv = devm_kzalloc(&pdev->dev, sizeof(*dwc_priv), GFP_KERNEL);
148 	if (!dwc_priv)
149 		return -ENOMEM;
150 
151 	/* Read rx-internal-delay-ps and update rx_clk delay */
152 	if (!of_property_read_u32(pdev->dev.of_node,
153 				  "rx-internal-delay-ps", &delay_ps)) {
154 		if (delay_ps % EIC7700_DELAY_STEP_PS)
155 			return dev_err_probe(&pdev->dev, -EINVAL,
156 				"rx delay must be multiple of %dps\n",
157 				EIC7700_DELAY_STEP_PS);
158 
159 		if (delay_ps > EIC7700_MAX_DELAY_PS)
160 			return dev_err_probe(&pdev->dev, -EINVAL,
161 				"rx delay out of range\n");
162 
163 		val = delay_ps / EIC7700_DELAY_STEP_PS;
164 
165 		dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY;
166 		dwc_priv->eth_clk_dly_param |=
167 				 FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val);
168 	} else {
169 		return dev_err_probe(&pdev->dev, -EINVAL,
170 			"missing required property rx-internal-delay-ps\n");
171 	}
172 
173 	/* Read tx-internal-delay-ps and update tx_clk delay */
174 	if (!of_property_read_u32(pdev->dev.of_node,
175 				  "tx-internal-delay-ps", &delay_ps)) {
176 		if (delay_ps % EIC7700_DELAY_STEP_PS)
177 			return dev_err_probe(&pdev->dev, -EINVAL,
178 				"tx delay must be multiple of %dps\n",
179 				EIC7700_DELAY_STEP_PS);
180 
181 		if (delay_ps > EIC7700_MAX_DELAY_PS)
182 			return dev_err_probe(&pdev->dev, -EINVAL,
183 				"tx delay out of range\n");
184 
185 		val = delay_ps / EIC7700_DELAY_STEP_PS;
186 
187 		dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY;
188 		dwc_priv->eth_clk_dly_param |=
189 				 FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val);
190 	} else {
191 		return dev_err_probe(&pdev->dev, -EINVAL,
192 			"missing required property tx-internal-delay-ps\n");
193 	}
194 
195 	dwc_priv->eic7700_hsp_regmap =
196 			syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
197 							"eswin,hsp-sp-csr");
198 	if (IS_ERR(dwc_priv->eic7700_hsp_regmap))
199 		return dev_err_probe(&pdev->dev,
200 				PTR_ERR(dwc_priv->eic7700_hsp_regmap),
201 				"Failed to get hsp-sp-csr regmap\n");
202 
203 	ret = of_property_read_u32_index(pdev->dev.of_node,
204 					 "eswin,hsp-sp-csr",
205 					 1, &dwc_priv->eth_phy_ctrl_offset);
206 	if (ret)
207 		return dev_err_probe(&pdev->dev, ret,
208 				     "can't get eth_phy_ctrl_offset\n");
209 
210 	ret = of_property_read_u32_index(pdev->dev.of_node,
211 					 "eswin,hsp-sp-csr",
212 					 2, &dwc_priv->eth_axi_lp_ctrl_offset);
213 	if (ret)
214 		return dev_err_probe(&pdev->dev, ret,
215 				     "can't get eth_axi_lp_ctrl_offset\n");
216 
217 	ret = of_property_read_u32_index(pdev->dev.of_node,
218 					 "eswin,hsp-sp-csr",
219 					 3, &dwc_priv->eth_clk_offset);
220 	if (ret)
221 		return dev_err_probe(&pdev->dev, ret,
222 				     "can't get eth_clk_offset\n");
223 
224 	ret = of_property_read_u32_index(pdev->dev.of_node,
225 					 "eswin,hsp-sp-csr",
226 					 4, &dwc_priv->eth_txd_offset);
227 	if (!ret)
228 		dwc_priv->has_txd_offset = true;
229 
230 	ret = of_property_read_u32_index(pdev->dev.of_node,
231 					 "eswin,hsp-sp-csr",
232 					 5, &dwc_priv->eth_rxd_offset);
233 	if (!ret)
234 		dwc_priv->has_rxd_offset = true;
235 
236 	plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names);
237 	plat_dat->clks = devm_kcalloc(&pdev->dev,
238 				      plat_dat->num_clks,
239 				      sizeof(*plat_dat->clks),
240 				      GFP_KERNEL);
241 	if (!plat_dat->clks)
242 		return -ENOMEM;
243 
244 	for (i = 0; i < ARRAY_SIZE(eic7700_clk_names); i++)
245 		plat_dat->clks[i].id = eic7700_clk_names[i];
246 
247 	ret = devm_clk_bulk_get_optional(&pdev->dev,
248 					 plat_dat->num_clks,
249 					 plat_dat->clks);
250 	if (ret)
251 		return dev_err_probe(&pdev->dev, ret,
252 				     "Failed to get clocks\n");
253 
254 	plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx");
255 	plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
256 	plat_dat->clks_config = eic7700_clks_config;
257 	plat_dat->bsp_priv = dwc_priv;
258 	dwc_priv->plat_dat = plat_dat;
259 	plat_dat->init = eic7700_dwmac_init;
260 	plat_dat->exit = eic7700_dwmac_exit;
261 	plat_dat->suspend = eic7700_dwmac_suspend;
262 	plat_dat->resume = eic7700_dwmac_resume;
263 
264 	return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
265 }
266 
267 static const struct of_device_id eic7700_dwmac_match[] = {
268 	{ .compatible = "eswin,eic7700-qos-eth" },
269 	{ }
270 };
271 MODULE_DEVICE_TABLE(of, eic7700_dwmac_match);
272 
273 static struct platform_driver eic7700_dwmac_driver = {
274 	.probe  = eic7700_dwmac_probe,
275 	.driver = {
276 		.name           = "eic7700-eth-dwmac",
277 		.pm             = &stmmac_pltfr_pm_ops,
278 		.of_match_table = eic7700_dwmac_match,
279 	},
280 };
281 module_platform_driver(eic7700_dwmac_driver);
282 
283 MODULE_AUTHOR("Zhi Li <lizhi2@eswincomputing.com>");
284 MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
285 MODULE_AUTHOR("Shangjuan Wei <weishangjuan@eswincomputing.com>");
286 MODULE_DESCRIPTION("Eswin eic7700 qos ethernet driver");
287 MODULE_LICENSE("GPL");
288