1// SPDX-License-Identifier: GPL-2.0-only
2#include <linux/platform_device.h>
3#include <linux/of.h>
4#include <linux/module.h>
5#include <linux/stmmac.h>
6#include <linux/clk.h>
7
8#include "stmmac_platform.h"
9
10static const char *const mgbe_clks[] = {
11 "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac"
12};
13
14struct tegra_mgbe {
15 struct device *dev;
16
17 struct clk_bulk_data *clks;
18
19 struct reset_control *rst_mac;
20 struct reset_control *rst_pcs;
21
22 void __iomem *hv;
23 void __iomem *regs;
24 void __iomem *xpcs;
25
26 struct mii_bus *mii;
27};
28
29#define XPCS_WRAP_UPHY_RX_CONTROL 0x801c
30#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD BIT(31)
31#define XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY BIT(10)
32#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET BIT(9)
33#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN BIT(8)
34#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP (BIT(7) | BIT(6))
35#define XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ BIT(5)
36#define XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ BIT(4)
37#define XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN BIT(0)
38#define XPCS_WRAP_UPHY_HW_INIT_CTRL 0x8020
39#define XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN BIT(0)
40#define XPCS_WRAP_UPHY_HW_INIT_CTRL_RX_EN BIT(2)
41#define XPCS_WRAP_UPHY_STATUS 0x8044
42#define XPCS_WRAP_UPHY_STATUS_TX_P_UP BIT(0)
43#define XPCS_WRAP_IRQ_STATUS 0x8050
44#define XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS BIT(6)
45
46#define XPCS_REG_ADDR_SHIFT 10
47#define XPCS_REG_ADDR_MASK 0x1fff
48#define XPCS_ADDR 0x3fc
49
50#define MGBE_WRAP_COMMON_INTR_ENABLE 0x8704
51#define MAC_SBD_INTR BIT(2)
52#define MGBE_WRAP_AXI_ASID0_CTRL 0x8400
53#define MGBE_SID 0x6
54
55static int __maybe_unused tegra_mgbe_suspend(struct device *dev)
56{
57 struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev);
58 int err;
59
60 err = stmmac_suspend(dev);
61 if (err)
62 return err;
63
64 clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
65
66 return reset_control_assert(rstc: mgbe->rst_mac);
67}
68
69static int __maybe_unused tegra_mgbe_resume(struct device *dev)
70{
71 struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev);
72 u32 value;
73 int err;
74
75 err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
76 if (err < 0)
77 return err;
78
79 err = reset_control_deassert(rstc: mgbe->rst_mac);
80 if (err < 0)
81 return err;
82
83 /* Enable common interrupt at wrapper level */
84 writel(MAC_SBD_INTR, addr: mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE);
85
86 /* Program SID */
87 writel(MGBE_SID, addr: mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL);
88
89 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_STATUS);
90 if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) {
91 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
92 value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN;
93 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
94 }
95
96 err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value,
97 (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0,
98 500, 500 * 2000);
99 if (err < 0) {
100 dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n");
101 clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
102 return err;
103 }
104
105 err = stmmac_resume(dev);
106 if (err < 0)
107 clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
108
109 return err;
110}
111
112static int mgbe_uphy_lane_bringup_serdes_up(struct net_device *ndev, void *mgbe_data)
113{
114 struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data;
115 u32 value;
116 int err;
117
118 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
119 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD;
120 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
121
122 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
123 value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ;
124 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
125
126 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
127 value &= ~XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ;
128 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
129
130 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
131 value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP;
132 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
133
134 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
135 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN;
136 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
137
138 err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL, value,
139 (value & XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN) == 0,
140 1000, 1000 * 2000);
141 if (err < 0) {
142 dev_err(mgbe->dev, "timeout waiting for RX calibration to become enabled\n");
143 return err;
144 }
145
146 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
147 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN;
148 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
149
150 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
151 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET;
152 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
153
154 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
155 value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET;
156 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
157
158 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
159 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY;
160 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
161
162 err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_IRQ_STATUS, value,
163 value & XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS,
164 500, 500 * 2000);
165 if (err < 0) {
166 dev_err(mgbe->dev, "timeout waiting for link to become ready\n");
167 return err;
168 }
169
170 /* clear status */
171 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_IRQ_STATUS);
172
173 return 0;
174}
175
176static void mgbe_uphy_lane_bringup_serdes_down(struct net_device *ndev, void *mgbe_data)
177{
178 struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data;
179 u32 value;
180
181 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
182 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD;
183 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
184
185 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
186 value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN;
187 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
188
189 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
190 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP;
191 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
192
193 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
194 value |= XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ;
195 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
196
197 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
198 value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ;
199 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
200}
201
202static int tegra_mgbe_probe(struct platform_device *pdev)
203{
204 struct plat_stmmacenet_data *plat;
205 struct stmmac_resources res;
206 struct tegra_mgbe *mgbe;
207 int irq, err, i;
208 u32 value;
209
210 mgbe = devm_kzalloc(dev: &pdev->dev, size: sizeof(*mgbe), GFP_KERNEL);
211 if (!mgbe)
212 return -ENOMEM;
213
214 mgbe->dev = &pdev->dev;
215
216 memset(&res, 0, sizeof(res));
217
218 irq = platform_get_irq(pdev, 0);
219 if (irq < 0)
220 return irq;
221
222 mgbe->hv = devm_platform_ioremap_resource_byname(pdev, name: "hypervisor");
223 if (IS_ERR(ptr: mgbe->hv))
224 return PTR_ERR(ptr: mgbe->hv);
225
226 mgbe->regs = devm_platform_ioremap_resource_byname(pdev, name: "mac");
227 if (IS_ERR(ptr: mgbe->regs))
228 return PTR_ERR(ptr: mgbe->regs);
229
230 mgbe->xpcs = devm_platform_ioremap_resource_byname(pdev, name: "xpcs");
231 if (IS_ERR(ptr: mgbe->xpcs))
232 return PTR_ERR(ptr: mgbe->xpcs);
233
234 res.addr = mgbe->regs;
235 res.irq = irq;
236
237 mgbe->clks = devm_kcalloc(dev: &pdev->dev, ARRAY_SIZE(mgbe_clks),
238 size: sizeof(*mgbe->clks), GFP_KERNEL);
239 if (!mgbe->clks)
240 return -ENOMEM;
241
242 for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++)
243 mgbe->clks[i].id = mgbe_clks[i];
244
245 err = devm_clk_bulk_get(dev: mgbe->dev, ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
246 if (err < 0)
247 return err;
248
249 err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
250 if (err < 0)
251 return err;
252
253 /* Perform MAC reset */
254 mgbe->rst_mac = devm_reset_control_get(dev: &pdev->dev, id: "mac");
255 if (IS_ERR(ptr: mgbe->rst_mac)) {
256 err = PTR_ERR(ptr: mgbe->rst_mac);
257 goto disable_clks;
258 }
259
260 err = reset_control_assert(rstc: mgbe->rst_mac);
261 if (err < 0)
262 goto disable_clks;
263
264 usleep_range(min: 2000, max: 4000);
265
266 err = reset_control_deassert(rstc: mgbe->rst_mac);
267 if (err < 0)
268 goto disable_clks;
269
270 /* Perform PCS reset */
271 mgbe->rst_pcs = devm_reset_control_get(dev: &pdev->dev, id: "pcs");
272 if (IS_ERR(ptr: mgbe->rst_pcs)) {
273 err = PTR_ERR(ptr: mgbe->rst_pcs);
274 goto disable_clks;
275 }
276
277 err = reset_control_assert(rstc: mgbe->rst_pcs);
278 if (err < 0)
279 goto disable_clks;
280
281 usleep_range(min: 2000, max: 4000);
282
283 err = reset_control_deassert(rstc: mgbe->rst_pcs);
284 if (err < 0)
285 goto disable_clks;
286
287 plat = devm_stmmac_probe_config_dt(pdev, mac: res.mac);
288 if (IS_ERR(ptr: plat)) {
289 err = PTR_ERR(ptr: plat);
290 goto disable_clks;
291 }
292
293 plat->has_xgmac = 1;
294 plat->flags |= STMMAC_FLAG_TSO_EN;
295 plat->pmt = 1;
296 plat->bsp_priv = mgbe;
297
298 if (!plat->mdio_node)
299 plat->mdio_node = of_get_child_by_name(node: pdev->dev.of_node, name: "mdio");
300
301 if (!plat->mdio_bus_data) {
302 plat->mdio_bus_data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat->mdio_bus_data),
303 GFP_KERNEL);
304 if (!plat->mdio_bus_data) {
305 err = -ENOMEM;
306 goto disable_clks;
307 }
308 }
309
310 plat->mdio_bus_data->needs_reset = true;
311
312 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_STATUS);
313 if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) {
314 value = readl(addr: mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
315 value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN;
316 writel(val: value, addr: mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
317 }
318
319 err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value,
320 (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0,
321 500, 500 * 2000);
322 if (err < 0) {
323 dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n");
324 goto disable_clks;
325 }
326
327 plat->serdes_powerup = mgbe_uphy_lane_bringup_serdes_up;
328 plat->serdes_powerdown = mgbe_uphy_lane_bringup_serdes_down;
329
330 /* Tx FIFO Size - 128KB */
331 plat->tx_fifo_size = 131072;
332 /* Rx FIFO Size - 192KB */
333 plat->rx_fifo_size = 196608;
334
335 /* Enable common interrupt at wrapper level */
336 writel(MAC_SBD_INTR, addr: mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE);
337
338 /* Program SID */
339 writel(MGBE_SID, addr: mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL);
340
341 plat->flags |= STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP;
342
343 err = stmmac_dvr_probe(device: &pdev->dev, plat_dat: plat, res: &res);
344 if (err < 0)
345 goto disable_clks;
346
347 return 0;
348
349disable_clks:
350 clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
351
352 return err;
353}
354
355static void tegra_mgbe_remove(struct platform_device *pdev)
356{
357 struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev: &pdev->dev);
358
359 clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), clks: mgbe->clks);
360
361 stmmac_pltfr_remove(pdev);
362}
363
364static const struct of_device_id tegra_mgbe_match[] = {
365 { .compatible = "nvidia,tegra234-mgbe", },
366 { }
367};
368MODULE_DEVICE_TABLE(of, tegra_mgbe_match);
369
370static SIMPLE_DEV_PM_OPS(tegra_mgbe_pm_ops, tegra_mgbe_suspend, tegra_mgbe_resume);
371
372static struct platform_driver tegra_mgbe_driver = {
373 .probe = tegra_mgbe_probe,
374 .remove_new = tegra_mgbe_remove,
375 .driver = {
376 .name = "tegra-mgbe",
377 .pm = &tegra_mgbe_pm_ops,
378 .of_match_table = tegra_mgbe_match,
379 },
380};
381module_platform_driver(tegra_mgbe_driver);
382
383MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
384MODULE_DESCRIPTION("NVIDIA Tegra MGBE driver");
385MODULE_LICENSE("GPL");
386

source code of linux/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c