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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c