Skip to content

Commit e6ea2d1

Browse files
thierryredingdavem330
authored andcommitted
net: stmmac: dwc-qos: Add Tegra186 support
The NVIDIA Tegra186 SoC contains an instance of the Synopsys DWC ethernet QOS IP core. The binding that it uses is slightly different from existing ones because of the integration (clocks, resets, ...). Signed-off-by: Thierry Reding <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent cee45b2 commit e6ea2d1

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,34 @@
1414
#include <linux/clk.h>
1515
#include <linux/clk-provider.h>
1616
#include <linux/device.h>
17+
#include <linux/gpio/consumer.h>
1718
#include <linux/ethtool.h>
1819
#include <linux/io.h>
20+
#include <linux/iopoll.h>
1921
#include <linux/ioport.h>
2022
#include <linux/module.h>
2123
#include <linux/of_device.h>
2224
#include <linux/of_net.h>
2325
#include <linux/mfd/syscon.h>
2426
#include <linux/platform_device.h>
27+
#include <linux/reset.h>
2528
#include <linux/stmmac.h>
2629

2730
#include "stmmac_platform.h"
31+
#include "dwmac4.h"
32+
33+
struct tegra_eqos {
34+
struct device *dev;
35+
void __iomem *regs;
36+
37+
struct reset_control *rst;
38+
struct clk *clk_master;
39+
struct clk *clk_slave;
40+
struct clk *clk_tx;
41+
struct clk *clk_rx;
42+
43+
struct gpio_desc *reset;
44+
};
2845

2946
static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
3047
struct plat_stmmacenet_data *plat_dat)
@@ -158,6 +175,230 @@ static int dwc_qos_remove(struct platform_device *pdev)
158175
return 0;
159176
}
160177

178+
#define SDMEMCOMPPADCTRL 0x8800
179+
#define SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31)
180+
181+
#define AUTO_CAL_CONFIG 0x8804
182+
#define AUTO_CAL_CONFIG_START BIT(31)
183+
#define AUTO_CAL_CONFIG_ENABLE BIT(29)
184+
185+
#define AUTO_CAL_STATUS 0x880c
186+
#define AUTO_CAL_STATUS_ACTIVE BIT(31)
187+
188+
static void tegra_eqos_fix_speed(void *priv, unsigned int speed)
189+
{
190+
struct tegra_eqos *eqos = priv;
191+
unsigned long rate = 125000000;
192+
bool needs_calibration = false;
193+
u32 value;
194+
int err;
195+
196+
switch (speed) {
197+
case SPEED_1000:
198+
needs_calibration = true;
199+
rate = 125000000;
200+
break;
201+
202+
case SPEED_100:
203+
needs_calibration = true;
204+
rate = 25000000;
205+
break;
206+
207+
case SPEED_10:
208+
rate = 2500000;
209+
break;
210+
211+
default:
212+
dev_err(eqos->dev, "invalid speed %u\n", speed);
213+
break;
214+
}
215+
216+
if (needs_calibration) {
217+
/* calibrate */
218+
value = readl(eqos->regs + SDMEMCOMPPADCTRL);
219+
value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
220+
writel(value, eqos->regs + SDMEMCOMPPADCTRL);
221+
222+
udelay(1);
223+
224+
value = readl(eqos->regs + AUTO_CAL_CONFIG);
225+
value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE;
226+
writel(value, eqos->regs + AUTO_CAL_CONFIG);
227+
228+
err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
229+
value,
230+
value & AUTO_CAL_STATUS_ACTIVE,
231+
1, 10);
232+
if (err < 0) {
233+
dev_err(eqos->dev, "calibration did not start\n");
234+
goto failed;
235+
}
236+
237+
err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS,
238+
value,
239+
(value & AUTO_CAL_STATUS_ACTIVE) == 0,
240+
20, 200);
241+
if (err < 0) {
242+
dev_err(eqos->dev, "calibration didn't finish\n");
243+
goto failed;
244+
}
245+
246+
failed:
247+
value = readl(eqos->regs + SDMEMCOMPPADCTRL);
248+
value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
249+
writel(value, eqos->regs + SDMEMCOMPPADCTRL);
250+
} else {
251+
value = readl(eqos->regs + AUTO_CAL_CONFIG);
252+
value &= ~AUTO_CAL_CONFIG_ENABLE;
253+
writel(value, eqos->regs + AUTO_CAL_CONFIG);
254+
}
255+
256+
err = clk_set_rate(eqos->clk_tx, rate);
257+
if (err < 0)
258+
dev_err(eqos->dev, "failed to set TX rate: %d\n", err);
259+
}
260+
261+
static int tegra_eqos_init(struct platform_device *pdev, void *priv)
262+
{
263+
struct tegra_eqos *eqos = priv;
264+
unsigned long rate;
265+
u32 value;
266+
267+
rate = clk_get_rate(eqos->clk_slave);
268+
269+
value = (rate / 1000000) - 1;
270+
writel(value, eqos->regs + GMAC_1US_TIC_COUNTER);
271+
272+
return 0;
273+
}
274+
275+
static void *tegra_eqos_probe(struct platform_device *pdev,
276+
struct plat_stmmacenet_data *data,
277+
struct stmmac_resources *res)
278+
{
279+
struct tegra_eqos *eqos;
280+
int err;
281+
282+
eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL);
283+
if (!eqos) {
284+
err = -ENOMEM;
285+
goto error;
286+
}
287+
288+
eqos->dev = &pdev->dev;
289+
eqos->regs = res->addr;
290+
291+
eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus");
292+
if (IS_ERR(eqos->clk_master)) {
293+
err = PTR_ERR(eqos->clk_master);
294+
goto error;
295+
}
296+
297+
err = clk_prepare_enable(eqos->clk_master);
298+
if (err < 0)
299+
goto error;
300+
301+
eqos->clk_slave = devm_clk_get(&pdev->dev, "slave_bus");
302+
if (IS_ERR(eqos->clk_slave)) {
303+
err = PTR_ERR(eqos->clk_slave);
304+
goto disable_master;
305+
}
306+
307+
data->stmmac_clk = eqos->clk_slave;
308+
309+
err = clk_prepare_enable(eqos->clk_slave);
310+
if (err < 0)
311+
goto disable_master;
312+
313+
eqos->clk_rx = devm_clk_get(&pdev->dev, "rx");
314+
if (IS_ERR(eqos->clk_rx)) {
315+
err = PTR_ERR(eqos->clk_rx);
316+
goto disable_slave;
317+
}
318+
319+
err = clk_prepare_enable(eqos->clk_rx);
320+
if (err < 0)
321+
goto disable_slave;
322+
323+
eqos->clk_tx = devm_clk_get(&pdev->dev, "tx");
324+
if (IS_ERR(eqos->clk_tx)) {
325+
err = PTR_ERR(eqos->clk_tx);
326+
goto disable_rx;
327+
}
328+
329+
err = clk_prepare_enable(eqos->clk_tx);
330+
if (err < 0)
331+
goto disable_rx;
332+
333+
eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH);
334+
if (IS_ERR(eqos->reset)) {
335+
err = PTR_ERR(eqos->reset);
336+
goto disable_tx;
337+
}
338+
339+
usleep_range(2000, 4000);
340+
gpiod_set_value(eqos->reset, 0);
341+
342+
eqos->rst = devm_reset_control_get(&pdev->dev, "eqos");
343+
if (IS_ERR(eqos->rst)) {
344+
err = PTR_ERR(eqos->rst);
345+
goto reset_phy;
346+
}
347+
348+
err = reset_control_assert(eqos->rst);
349+
if (err < 0)
350+
goto reset_phy;
351+
352+
usleep_range(2000, 4000);
353+
354+
err = reset_control_deassert(eqos->rst);
355+
if (err < 0)
356+
goto reset_phy;
357+
358+
usleep_range(2000, 4000);
359+
360+
data->fix_mac_speed = tegra_eqos_fix_speed;
361+
data->init = tegra_eqos_init;
362+
data->bsp_priv = eqos;
363+
364+
err = tegra_eqos_init(pdev, eqos);
365+
if (err < 0)
366+
goto reset;
367+
368+
out:
369+
return eqos;
370+
371+
reset:
372+
reset_control_assert(eqos->rst);
373+
reset_phy:
374+
gpiod_set_value(eqos->reset, 1);
375+
disable_tx:
376+
clk_disable_unprepare(eqos->clk_tx);
377+
disable_rx:
378+
clk_disable_unprepare(eqos->clk_rx);
379+
disable_slave:
380+
clk_disable_unprepare(eqos->clk_slave);
381+
disable_master:
382+
clk_disable_unprepare(eqos->clk_master);
383+
error:
384+
eqos = ERR_PTR(err);
385+
goto out;
386+
}
387+
388+
static int tegra_eqos_remove(struct platform_device *pdev)
389+
{
390+
struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev);
391+
392+
reset_control_assert(eqos->rst);
393+
gpiod_set_value(eqos->reset, 1);
394+
clk_disable_unprepare(eqos->clk_tx);
395+
clk_disable_unprepare(eqos->clk_rx);
396+
clk_disable_unprepare(eqos->clk_slave);
397+
clk_disable_unprepare(eqos->clk_master);
398+
399+
return 0;
400+
}
401+
161402
struct dwc_eth_dwmac_data {
162403
void *(*probe)(struct platform_device *pdev,
163404
struct plat_stmmacenet_data *data,
@@ -170,6 +411,11 @@ static const struct dwc_eth_dwmac_data dwc_qos_data = {
170411
.remove = dwc_qos_remove,
171412
};
172413

414+
static const struct dwc_eth_dwmac_data tegra_eqos_data = {
415+
.probe = tegra_eqos_probe,
416+
.remove = tegra_eqos_remove,
417+
};
418+
173419
static int dwc_eth_dwmac_probe(struct platform_device *pdev)
174420
{
175421
const struct dwc_eth_dwmac_data *data;
@@ -255,6 +501,7 @@ static int dwc_eth_dwmac_remove(struct platform_device *pdev)
255501

256502
static const struct of_device_id dwc_eth_dwmac_match[] = {
257503
{ .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data },
504+
{ .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data },
258505
{ }
259506
};
260507
MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match);

drivers/net/ethernet/stmicro/stmmac/dwmac4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#define GMAC_RXQ_CTRL0 0x000000a0
2626
#define GMAC_INT_STATUS 0x000000b0
2727
#define GMAC_INT_EN 0x000000b4
28+
#define GMAC_1US_TIC_COUNTER 0x000000dc
2829
#define GMAC_PCS_BASE 0x000000e0
2930
#define GMAC_PHYIF_CONTROL_STATUS 0x000000f8
3031
#define GMAC_PMT 0x000000c0

0 commit comments

Comments
 (0)