File Index
Files and directories
Sign in
FILE

dwmac-tegra.c

File preview with syntax highlighting.

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