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