File Index
Files and directories
Sign in
FILE

dwmac-dwc-qos-eth.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Synopsys DWC Ethernet Quality-of-Service v4.10a linux driver
  4 *
  5 * Copyright (C) 2016 Joao Pinto <jpinto@synopsys.com>
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/clk-provider.h>
 10#include <linux/device.h>
 11#include <linux/gpio/consumer.h>
 12#include <linux/ethtool.h>
 13#include <linux/io.h>
 14#include <linux/iopoll.h>
 15#include <linux/ioport.h>
 16#include <linux/module.h>
 17#include <linux/of.h>
 18#include <linux/of_net.h>
 19#include <linux/mfd/syscon.h>
 20#include <linux/platform_device.h>
 21#include <linux/reset.h>
 22#include <linux/stmmac.h>
 23
 24#include "stmmac_platform.h"
 25#include "dwmac4.h"
 26
 27struct tegra_eqos {
 28	struct device *dev;
 29	void __iomem *regs;
 30
 31	struct reset_control *rst;
 32
 33	struct gpio_desc *reset;
 34};
 35
 36static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
 37				   struct plat_stmmacenet_data *plat_dat)
 38{
 39	struct device *dev = &pdev->dev;
 40	u32 burst_map = 0;
 41
 42	if (!plat_dat->axi) {
 43		plat_dat->axi = devm_kzalloc(&pdev->dev,
 44					     sizeof(struct stmmac_axi),
 45					     GFP_KERNEL);
 46
 47		if (!plat_dat->axi)
 48			return -ENOMEM;
 49	}
 50
 51	plat_dat->axi->axi_lpi_en = device_property_read_bool(dev,
 52							      "snps,en-lpi");
 53	if (device_property_read_u32(dev, "snps,write-requests",
 54				     &plat_dat->axi->axi_wr_osr_lmt)) {
 55		/**
 56		 * Since the register has a reset value of 1, if property
 57		 * is missing, default to 1.
 58		 */
 59		plat_dat->axi->axi_wr_osr_lmt = 1;
 60	} else {
 61		/**
 62		 * If property exists, to keep the behavior from dwc_eth_qos,
 63		 * subtract one after parsing.
 64		 */
 65		plat_dat->axi->axi_wr_osr_lmt--;
 66	}
 67
 68	if (device_property_read_u32(dev, "snps,read-requests",
 69				     &plat_dat->axi->axi_rd_osr_lmt)) {
 70		/**
 71		 * Since the register has a reset value of 1, if property
 72		 * is missing, default to 1.
 73		 */
 74		plat_dat->axi->axi_rd_osr_lmt = 1;
 75	} else {
 76		/**
 77		 * If property exists, to keep the behavior from dwc_eth_qos,
 78		 * subtract one after parsing.
 79		 */
 80		plat_dat->axi->axi_rd_osr_lmt--;
 81	}
 82	device_property_read_u32(dev, "snps,burst-map", &burst_map);
 83
 84	plat_dat->axi->axi_blen_regval = FIELD_PREP(DMA_AXI_BLEN_MASK,
 85						    burst_map);
 86
 87	/* dwc-qos needs GMAC4, AAL, TSO and PMT */
 88	plat_dat->core_type = DWMAC_CORE_GMAC4;
 89	plat_dat->dma_cfg->aal = 1;
 90	plat_dat->flags |= STMMAC_FLAG_TSO_EN;
 91	plat_dat->pmt = 1;
 92
 93	return 0;
 94}
 95
 96static int dwc_qos_probe(struct platform_device *pdev,
 97			 struct plat_stmmacenet_data *plat_dat,
 98			 struct stmmac_resources *stmmac_res)
 99{
100	plat_dat->pclk = stmmac_pltfr_find_clk(plat_dat, "phy_ref_clk");
101
102	return 0;
103}
104
105#define SDMEMCOMPPADCTRL 0x8800
106#define  SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31)
107
108#define AUTO_CAL_CONFIG 0x8804
109#define  AUTO_CAL_CONFIG_START BIT(31)
110#define  AUTO_CAL_CONFIG_ENABLE BIT(29)
111
112#define AUTO_CAL_STATUS 0x880c
113#define  AUTO_CAL_STATUS_ACTIVE BIT(31)
114
115static void tegra_eqos_fix_speed(void *bsp_priv, int speed, unsigned int mode)
116{
117	struct tegra_eqos *eqos = bsp_priv;
118	bool needs_calibration = false;
119	struct stmmac_priv *priv;
120	u32 value;
121	int err;
122
123	switch (speed) {
124	case SPEED_1000:
125	case SPEED_100:
126		needs_calibration = true;
127		fallthrough;
128
129	case SPEED_10:
130		break;
131
132	default:
133		dev_err(eqos->dev, "invalid speed %d\n", speed);
134		break;
135	}
136
137	if (needs_calibration) {
138		priv = netdev_priv(dev_get_drvdata(eqos->dev));
139
140		/* Calibration should be done with the MDIO bus idle */
141		stmmac_mdio_lock(priv);
142
143		/* calibrate */
144		value = readl(eqos->regs + SDMEMCOMPPADCTRL);
145		value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
146		writel(value, eqos->regs + SDMEMCOMPPADCTRL);
147
148		udelay(1);
149
150		value = readl(eqos->regs + AUTO_CAL_CONFIG);
151		value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE;
152		writel(value, eqos->regs + AUTO_CAL_CONFIG);
153
154		err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
155						value,
156						value & AUTO_CAL_STATUS_ACTIVE,
157						1, 10);
158		if (err < 0) {
159			dev_err(eqos->dev, "calibration did not start\n");
160			goto failed;
161		}
162
163		err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
164						value,
165						(value & AUTO_CAL_STATUS_ACTIVE) == 0,
166						20, 200);
167		if (err < 0) {
168			dev_err(eqos->dev, "calibration didn't finish\n");
169			goto failed;
170		}
171
172	failed:
173		value = readl(eqos->regs + SDMEMCOMPPADCTRL);
174		value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
175		writel(value, eqos->regs + SDMEMCOMPPADCTRL);
176
177		stmmac_mdio_unlock(priv);
178	} else {
179		value = readl(eqos->regs + AUTO_CAL_CONFIG);
180		value &= ~AUTO_CAL_CONFIG_ENABLE;
181		writel(value, eqos->regs + AUTO_CAL_CONFIG);
182	}
183}
184
185static int tegra_eqos_probe(struct platform_device *pdev,
186			    struct plat_stmmacenet_data *plat_dat,
187			    struct stmmac_resources *res)
188{
189	struct device *dev = &pdev->dev;
190	struct tegra_eqos *eqos;
191	int err;
192
193	eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL);
194	if (!eqos)
195		return -ENOMEM;
196
197	eqos->dev = &pdev->dev;
198	eqos->regs = res->addr;
199
200	if (!is_of_node(dev->fwnode))
201		goto bypass_clk_reset_gpio;
202
203	plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx");
204
205	eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH);
206	if (IS_ERR(eqos->reset)) {
207		err = PTR_ERR(eqos->reset);
208		return err;
209	}
210
211	usleep_range(2000, 4000);
212	gpiod_set_value(eqos->reset, 0);
213
214	/* MDIO bus was already reset just above */
215	plat_dat->mdio_bus_data->needs_reset = false;
216
217	eqos->rst = devm_reset_control_get(&pdev->dev, "eqos");
218	if (IS_ERR(eqos->rst)) {
219		err = PTR_ERR(eqos->rst);
220		goto reset_phy;
221	}
222
223	err = reset_control_assert(eqos->rst);
224	if (err < 0)
225		goto reset_phy;
226
227	usleep_range(2000, 4000);
228
229	err = reset_control_deassert(eqos->rst);
230	if (err < 0)
231		goto reset_phy;
232
233	usleep_range(2000, 4000);
234
235bypass_clk_reset_gpio:
236	plat_dat->fix_mac_speed = tegra_eqos_fix_speed;
237	plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
238	plat_dat->bsp_priv = eqos;
239	plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE |
240			   STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP |
241			   STMMAC_FLAG_USE_PHY_WOL;
242
243	return 0;
244
245reset_phy:
246	gpiod_set_value(eqos->reset, 1);
247
248	return err;
249}
250
251static void tegra_eqos_remove(struct platform_device *pdev)
252{
253	struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev);
254
255	reset_control_assert(eqos->rst);
256	gpiod_set_value(eqos->reset, 1);
257}
258
259struct dwc_eth_dwmac_data {
260	int (*probe)(struct platform_device *pdev,
261		     struct plat_stmmacenet_data *plat_dat,
262		     struct stmmac_resources *res);
263	void (*remove)(struct platform_device *pdev);
264	const char *stmmac_clk_name;
265};
266
267static const struct dwc_eth_dwmac_data dwc_qos_data = {
268	.probe = dwc_qos_probe,
269	.stmmac_clk_name = "apb_pclk",
270};
271
272static const struct dwc_eth_dwmac_data tegra_eqos_data = {
273	.probe = tegra_eqos_probe,
274	.remove = tegra_eqos_remove,
275	.stmmac_clk_name = "slave_bus",
276};
277
278static const struct dwc_eth_dwmac_data fsd_eqos_data = {
279	.stmmac_clk_name = "slave_bus",
280};
281
282static int dwc_eth_dwmac_probe(struct platform_device *pdev)
283{
284	const struct dwc_eth_dwmac_data *data;
285	struct plat_stmmacenet_data *plat_dat;
286	struct stmmac_resources stmmac_res;
287	int ret;
288
289	data = device_get_match_data(&pdev->dev);
290
291	memset(&stmmac_res, 0, sizeof(struct stmmac_resources));
292
293	/**
294	 * Since stmmac_platform supports name IRQ only, basic platform
295	 * resource initialization is done in the glue logic.
296	 */
297	stmmac_res.irq = platform_get_irq(pdev, 0);
298	if (stmmac_res.irq < 0)
299		return stmmac_res.irq;
300	stmmac_res.wol_irq = stmmac_res.irq;
301
302	stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0);
303	if (IS_ERR(stmmac_res.addr))
304		return PTR_ERR(stmmac_res.addr);
305
306	plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
307	if (IS_ERR(plat_dat))
308		return PTR_ERR(plat_dat);
309
310	ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &plat_dat->clks);
311	if (ret < 0)
312		return dev_err_probe(&pdev->dev, ret, "Failed to retrieve and enable all required clocks\n");
313	plat_dat->num_clks = ret;
314
315	plat_dat->stmmac_clk = stmmac_pltfr_find_clk(plat_dat,
316						     data->stmmac_clk_name);
317
318	if (data->probe)
319		ret = data->probe(pdev, plat_dat, &stmmac_res);
320	if (ret < 0) {
321		dev_err_probe(&pdev->dev, ret, "failed to probe subdriver\n");
322		return ret;
323	}
324
325	ret = dwc_eth_dwmac_config_dt(pdev, plat_dat);
326	if (ret)
327		goto remove;
328
329	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
330	if (ret)
331		goto remove;
332
333	return ret;
334
335remove:
336	if (data->remove)
337		data->remove(pdev);
338
339	return ret;
340}
341
342static void dwc_eth_dwmac_remove(struct platform_device *pdev)
343{
344	const struct dwc_eth_dwmac_data *data = device_get_match_data(&pdev->dev);
345
346	stmmac_dvr_remove(&pdev->dev);
347
348	if (data->remove)
349		data->remove(pdev);
350}
351
352static const struct of_device_id dwc_eth_dwmac_match[] = {
353	{ .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data },
354	{ .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data },
355	{ .compatible = "tesla,fsd-ethqos", .data = &fsd_eqos_data },
356	{ }
357};
358MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match);
359
360static struct platform_driver dwc_eth_dwmac_driver = {
361	.probe  = dwc_eth_dwmac_probe,
362	.remove = dwc_eth_dwmac_remove,
363	.driver = {
364		.name           = "dwc-eth-dwmac",
365		.pm             = &stmmac_pltfr_pm_ops,
366		.of_match_table = dwc_eth_dwmac_match,
367	},
368};
369module_platform_driver(dwc_eth_dwmac_driver);
370
371MODULE_AUTHOR("Joao Pinto <jpinto@synopsys.com>");
372MODULE_DESCRIPTION("Synopsys DWC Ethernet Quality-of-Service v4.10a driver");
373MODULE_LICENSE("GPL v2");
~/ Read-only file preview.