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(dev: &pdev->dev,
44 size: 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 propname: "snps,en-lpi");
53 if (device_property_read_u32(dev, propname: "snps,write-requests",
54 val: &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, propname: "snps,read-requests",
69 val: &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, propname: "snps,burst-map", val: &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, name: "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: dev_get_drvdata(dev: eqos->dev));
139
140 /* Calibration should be done with the MDIO bus idle */
141 stmmac_mdio_lock(priv);
142
143 /* calibrate */
144 value = readl(addr: eqos->regs + SDMEMCOMPPADCTRL);
145 value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
146 writel(val: value, addr: eqos->regs + SDMEMCOMPPADCTRL);
147
148 udelay(usec: 1);
149
150 value = readl(addr: eqos->regs + AUTO_CAL_CONFIG);
151 value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE;
152 writel(val: value, addr: 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(addr: eqos->regs + SDMEMCOMPPADCTRL);
174 value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
175 writel(val: value, addr: eqos->regs + SDMEMCOMPPADCTRL);
176
177 stmmac_mdio_unlock(priv);
178 } else {
179 value = readl(addr: eqos->regs + AUTO_CAL_CONFIG);
180 value &= ~AUTO_CAL_CONFIG_ENABLE;
181 writel(val: value, addr: 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(dev: &pdev->dev, size: 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(fwnode: dev->fwnode))
201 goto bypass_clk_reset_gpio;
202
203 plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, name: "tx");
204
205 eqos->reset = devm_gpiod_get(dev: &pdev->dev, con_id: "phy-reset", flags: GPIOD_OUT_HIGH);
206 if (IS_ERR(ptr: eqos->reset)) {
207 err = PTR_ERR(ptr: eqos->reset);
208 return err;
209 }
210
211 usleep_range(min: 2000, max: 4000);
212 gpiod_set_value(desc: eqos->reset, value: 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(dev: &pdev->dev, id: "eqos");
218 if (IS_ERR(ptr: eqos->rst)) {
219 err = PTR_ERR(ptr: eqos->rst);
220 goto reset_phy;
221 }
222
223 err = reset_control_assert(rstc: eqos->rst);
224 if (err < 0)
225 goto reset_phy;
226
227 usleep_range(min: 2000, max: 4000);
228
229 err = reset_control_deassert(rstc: eqos->rst);
230 if (err < 0)
231 goto reset_phy;
232
233 usleep_range(min: 2000, max: 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(desc: eqos->reset, value: 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(dev: &pdev->dev);
254
255 reset_control_assert(rstc: eqos->rst);
256 gpiod_set_value(desc: eqos->reset, value: 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(dev: &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, index: 0);
303 if (IS_ERR(ptr: stmmac_res.addr))
304 return PTR_ERR(ptr: stmmac_res.addr);
305
306 plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac);
307 if (IS_ERR(ptr: plat_dat))
308 return PTR_ERR(ptr: plat_dat);
309
310 ret = devm_clk_bulk_get_all_enabled(dev: &pdev->dev, clks: &plat_dat->clks);
311 if (ret < 0)
312 return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "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 name: 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(dev: &pdev->dev, err: ret, fmt: "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(device: &pdev->dev, plat_dat, res: &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(dev: &pdev->dev);
345
346 stmmac_dvr_remove(dev: &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");
374

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