1 // SPDX-License-Identifier: GPL-2.0-only
3 * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
5 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
8 #include <linux/bitfield.h>
10 #include <linux/clk-provider.h>
11 #include <linux/device.h>
12 #include <linux/ethtool.h>
14 #include <linux/ioport.h>
15 #include <linux/module.h>
16 #include <linux/of_device.h>
17 #include <linux/of_net.h>
18 #include <linux/mfd/syscon.h>
19 #include <linux/platform_device.h>
20 #include <linux/stmmac.h>
22 #include "stmmac_platform.h"
26 #define PRG_ETH0_RGMII_MODE BIT(0)
28 #define PRG_ETH0_EXT_PHY_MODE_MASK GENMASK(2, 0)
29 #define PRG_ETH0_EXT_RGMII_MODE 1
30 #define PRG_ETH0_EXT_RMII_MODE 4
32 /* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
33 #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
35 /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where 8ns are exactly one
36 * cycle of the 125MHz RGMII TX clock):
37 * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
39 #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5)
41 /* divider for the result of m250_sel */
42 #define PRG_ETH0_CLK_M250_DIV_SHIFT 7
43 #define PRG_ETH0_CLK_M250_DIV_WIDTH 3
45 #define PRG_ETH0_RGMII_TX_CLK_EN 10
47 #define PRG_ETH0_INVERTED_RMII_CLK BIT(11)
48 #define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12)
50 /* Bypass (= 0, the signal from the GPIO input directly connects to the
51 * internal sampling) or enable (= 1) the internal logic for RXEN and RXD[3:0]
54 #define PRG_ETH0_ADJ_ENABLE BIT(13)
55 /* Controls whether the RXEN and RXD[3:0] signals should be aligned with the
56 * input RX rising/falling edge and sent to the Ethernet internals. This sets
57 * the automatically delay and skew automatically (internally).
59 #define PRG_ETH0_ADJ_SETUP BIT(14)
60 /* An internal counter based on the "timing-adjustment" clock. The counter is
61 * cleared on both, the falling and rising edge of the RX_CLK. This selects the
62 * delay (= the counter value) when to start sampling RXEN and RXD[3:0].
64 #define PRG_ETH0_ADJ_DELAY GENMASK(19, 15)
65 /* Adjusts the skew between each bit of RXEN and RXD[3:0]. If a signal has a
66 * large input delay, the bit for that signal (RXEN = bit 0, RXD[3] = bit 1,
67 * ...) can be configured to be 1 to compensate for a delay of about 1ns.
69 #define PRG_ETH0_ADJ_SKEW GENMASK(24, 20)
73 struct meson8b_dwmac_data
{
74 int (*set_phy_mode
)(struct meson8b_dwmac
*dwmac
);
77 struct meson8b_dwmac
{
81 const struct meson8b_dwmac_data
*data
;
82 phy_interface_t phy_mode
;
83 struct clk
*rgmii_tx_clk
;
86 struct clk
*timing_adj_clk
;
89 struct meson8b_dwmac_clk_configs
{
90 struct clk_mux m250_mux
;
91 struct clk_divider m250_div
;
92 struct clk_fixed_factor fixed_div2
;
93 struct clk_gate rgmii_tx_en
;
96 static void meson8b_dwmac_mask_bits(struct meson8b_dwmac
*dwmac
, u32 reg
,
101 data
= readl(dwmac
->regs
+ reg
);
103 data
|= (value
& mask
);
105 writel(data
, dwmac
->regs
+ reg
);
108 static struct clk
*meson8b_dwmac_register_clk(struct meson8b_dwmac
*dwmac
,
109 const char *name_suffix
,
110 const struct clk_parent_data
*parents
,
112 const struct clk_ops
*ops
,
115 struct clk_init_data init
= { };
118 snprintf(clk_name
, sizeof(clk_name
), "%s#%s", dev_name(dwmac
->dev
),
121 init
.name
= clk_name
;
123 init
.flags
= CLK_SET_RATE_PARENT
;
124 init
.parent_data
= parents
;
125 init
.num_parents
= num_parents
;
129 return devm_clk_register(dwmac
->dev
, hw
);
132 static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac
*dwmac
)
135 struct device
*dev
= dwmac
->dev
;
136 static const struct clk_parent_data mux_parents
[] = {
137 { .fw_name
= "clkin0", },
140 static const struct clk_div_table div_table
[] = {
141 { .div
= 2, .val
= 2, },
142 { .div
= 3, .val
= 3, },
143 { .div
= 4, .val
= 4, },
144 { .div
= 5, .val
= 5, },
145 { .div
= 6, .val
= 6, },
146 { .div
= 7, .val
= 7, },
147 { /* end of array */ }
149 struct meson8b_dwmac_clk_configs
*clk_configs
;
150 struct clk_parent_data parent_data
= { };
152 clk_configs
= devm_kzalloc(dev
, sizeof(*clk_configs
), GFP_KERNEL
);
156 clk_configs
->m250_mux
.reg
= dwmac
->regs
+ PRG_ETH0
;
157 clk_configs
->m250_mux
.shift
= __ffs(PRG_ETH0_CLK_M250_SEL_MASK
);
158 clk_configs
->m250_mux
.mask
= PRG_ETH0_CLK_M250_SEL_MASK
>>
159 clk_configs
->m250_mux
.shift
;
160 clk
= meson8b_dwmac_register_clk(dwmac
, "m250_sel", mux_parents
,
161 ARRAY_SIZE(mux_parents
), &clk_mux_ops
,
162 &clk_configs
->m250_mux
.hw
);
163 if (WARN_ON(IS_ERR(clk
)))
166 parent_data
.hw
= &clk_configs
->m250_mux
.hw
;
167 clk_configs
->m250_div
.reg
= dwmac
->regs
+ PRG_ETH0
;
168 clk_configs
->m250_div
.shift
= PRG_ETH0_CLK_M250_DIV_SHIFT
;
169 clk_configs
->m250_div
.width
= PRG_ETH0_CLK_M250_DIV_WIDTH
;
170 clk_configs
->m250_div
.table
= div_table
;
171 clk_configs
->m250_div
.flags
= CLK_DIVIDER_ALLOW_ZERO
|
172 CLK_DIVIDER_ROUND_CLOSEST
;
173 clk
= meson8b_dwmac_register_clk(dwmac
, "m250_div", &parent_data
, 1,
175 &clk_configs
->m250_div
.hw
);
176 if (WARN_ON(IS_ERR(clk
)))
179 parent_data
.hw
= &clk_configs
->m250_div
.hw
;
180 clk_configs
->fixed_div2
.mult
= 1;
181 clk_configs
->fixed_div2
.div
= 2;
182 clk
= meson8b_dwmac_register_clk(dwmac
, "fixed_div2", &parent_data
, 1,
183 &clk_fixed_factor_ops
,
184 &clk_configs
->fixed_div2
.hw
);
185 if (WARN_ON(IS_ERR(clk
)))
188 parent_data
.hw
= &clk_configs
->fixed_div2
.hw
;
189 clk_configs
->rgmii_tx_en
.reg
= dwmac
->regs
+ PRG_ETH0
;
190 clk_configs
->rgmii_tx_en
.bit_idx
= PRG_ETH0_RGMII_TX_CLK_EN
;
191 clk
= meson8b_dwmac_register_clk(dwmac
, "rgmii_tx_en", &parent_data
, 1,
193 &clk_configs
->rgmii_tx_en
.hw
);
194 if (WARN_ON(IS_ERR(clk
)))
197 dwmac
->rgmii_tx_clk
= clk
;
202 static int meson8b_set_phy_mode(struct meson8b_dwmac
*dwmac
)
204 switch (dwmac
->phy_mode
) {
205 case PHY_INTERFACE_MODE_RGMII
:
206 case PHY_INTERFACE_MODE_RGMII_RXID
:
207 case PHY_INTERFACE_MODE_RGMII_ID
:
208 case PHY_INTERFACE_MODE_RGMII_TXID
:
209 /* enable RGMII mode */
210 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
212 PRG_ETH0_RGMII_MODE
);
214 case PHY_INTERFACE_MODE_RMII
:
215 /* disable RGMII mode -> enables RMII mode */
216 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
217 PRG_ETH0_RGMII_MODE
, 0);
220 dev_err(dwmac
->dev
, "fail to set phy-mode %s\n",
221 phy_modes(dwmac
->phy_mode
));
228 static int meson_axg_set_phy_mode(struct meson8b_dwmac
*dwmac
)
230 switch (dwmac
->phy_mode
) {
231 case PHY_INTERFACE_MODE_RGMII
:
232 case PHY_INTERFACE_MODE_RGMII_RXID
:
233 case PHY_INTERFACE_MODE_RGMII_ID
:
234 case PHY_INTERFACE_MODE_RGMII_TXID
:
235 /* enable RGMII mode */
236 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
237 PRG_ETH0_EXT_PHY_MODE_MASK
,
238 PRG_ETH0_EXT_RGMII_MODE
);
240 case PHY_INTERFACE_MODE_RMII
:
241 /* disable RGMII mode -> enables RMII mode */
242 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
243 PRG_ETH0_EXT_PHY_MODE_MASK
,
244 PRG_ETH0_EXT_RMII_MODE
);
247 dev_err(dwmac
->dev
, "fail to set phy-mode %s\n",
248 phy_modes(dwmac
->phy_mode
));
255 static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac
*dwmac
,
260 ret
= clk_prepare_enable(clk
);
264 devm_add_action_or_reset(dwmac
->dev
,
265 (void(*)(void *))clk_disable_unprepare
,
266 dwmac
->rgmii_tx_clk
);
271 static int meson8b_init_prg_eth(struct meson8b_dwmac
*dwmac
)
273 u32 tx_dly_config
, rx_dly_config
, delay_config
;
276 tx_dly_config
= FIELD_PREP(PRG_ETH0_TXDLY_MASK
,
277 dwmac
->tx_delay_ns
>> 1);
279 if (dwmac
->rx_delay_ns
== 2)
280 rx_dly_config
= PRG_ETH0_ADJ_ENABLE
| PRG_ETH0_ADJ_SETUP
;
284 switch (dwmac
->phy_mode
) {
285 case PHY_INTERFACE_MODE_RGMII
:
286 delay_config
= tx_dly_config
| rx_dly_config
;
288 case PHY_INTERFACE_MODE_RGMII_RXID
:
289 delay_config
= tx_dly_config
;
291 case PHY_INTERFACE_MODE_RGMII_TXID
:
292 delay_config
= rx_dly_config
;
294 case PHY_INTERFACE_MODE_RGMII_ID
:
295 case PHY_INTERFACE_MODE_RMII
:
299 dev_err(dwmac
->dev
, "unsupported phy-mode %s\n",
300 phy_modes(dwmac
->phy_mode
));
304 if (rx_dly_config
& PRG_ETH0_ADJ_ENABLE
) {
305 if (!dwmac
->timing_adj_clk
) {
307 "The timing-adjustment clock is mandatory for the RX delay re-timing\n");
311 /* The timing adjustment logic is driven by a separate clock */
312 ret
= meson8b_devm_clk_prepare_enable(dwmac
,
313 dwmac
->timing_adj_clk
);
316 "Failed to enable the timing-adjustment clock\n");
321 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
, PRG_ETH0_TXDLY_MASK
|
322 PRG_ETH0_ADJ_ENABLE
| PRG_ETH0_ADJ_SETUP
|
323 PRG_ETH0_ADJ_DELAY
| PRG_ETH0_ADJ_SKEW
,
326 if (phy_interface_mode_is_rgmii(dwmac
->phy_mode
)) {
327 /* only relevant for RMII mode -> disable in RGMII mode */
328 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
329 PRG_ETH0_INVERTED_RMII_CLK
, 0);
331 /* Configure the 125MHz RGMII TX clock, the IP block changes
332 * the output automatically (= without us having to configure
333 * a register) based on the line-speed (125MHz for Gbit speeds,
334 * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
336 ret
= clk_set_rate(dwmac
->rgmii_tx_clk
, 125 * 1000 * 1000);
339 "failed to set RGMII TX clock\n");
343 ret
= meson8b_devm_clk_prepare_enable(dwmac
,
344 dwmac
->rgmii_tx_clk
);
347 "failed to enable the RGMII TX clock\n");
351 /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
352 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
,
353 PRG_ETH0_INVERTED_RMII_CLK
,
354 PRG_ETH0_INVERTED_RMII_CLK
);
357 /* enable TX_CLK and PHY_REF_CLK generator */
358 meson8b_dwmac_mask_bits(dwmac
, PRG_ETH0
, PRG_ETH0_TX_AND_PHY_REF_CLK
,
359 PRG_ETH0_TX_AND_PHY_REF_CLK
);
364 static int meson8b_dwmac_probe(struct platform_device
*pdev
)
366 struct plat_stmmacenet_data
*plat_dat
;
367 struct stmmac_resources stmmac_res
;
368 struct meson8b_dwmac
*dwmac
;
371 ret
= stmmac_get_platform_resources(pdev
, &stmmac_res
);
375 plat_dat
= stmmac_probe_config_dt(pdev
, &stmmac_res
.mac
);
376 if (IS_ERR(plat_dat
))
377 return PTR_ERR(plat_dat
);
379 dwmac
= devm_kzalloc(&pdev
->dev
, sizeof(*dwmac
), GFP_KERNEL
);
382 goto err_remove_config_dt
;
385 dwmac
->data
= (const struct meson8b_dwmac_data
*)
386 of_device_get_match_data(&pdev
->dev
);
389 goto err_remove_config_dt
;
391 dwmac
->regs
= devm_platform_ioremap_resource(pdev
, 1);
392 if (IS_ERR(dwmac
->regs
)) {
393 ret
= PTR_ERR(dwmac
->regs
);
394 goto err_remove_config_dt
;
397 dwmac
->dev
= &pdev
->dev
;
398 ret
= of_get_phy_mode(pdev
->dev
.of_node
, &dwmac
->phy_mode
);
400 dev_err(&pdev
->dev
, "missing phy-mode property\n");
401 goto err_remove_config_dt
;
404 /* use 2ns as fallback since this value was previously hardcoded */
405 if (of_property_read_u32(pdev
->dev
.of_node
, "amlogic,tx-delay-ns",
406 &dwmac
->tx_delay_ns
))
407 dwmac
->tx_delay_ns
= 2;
409 /* use 0ns as fallback since this is what most boards actually use */
410 if (of_property_read_u32(pdev
->dev
.of_node
, "amlogic,rx-delay-ns",
411 &dwmac
->rx_delay_ns
))
412 dwmac
->rx_delay_ns
= 0;
414 if (dwmac
->rx_delay_ns
!= 0 && dwmac
->rx_delay_ns
!= 2) {
416 "The only allowed RX delays values are: 0ns, 2ns");
418 goto err_remove_config_dt
;
421 dwmac
->timing_adj_clk
= devm_clk_get_optional(dwmac
->dev
,
422 "timing-adjustment");
423 if (IS_ERR(dwmac
->timing_adj_clk
)) {
424 ret
= PTR_ERR(dwmac
->timing_adj_clk
);
425 goto err_remove_config_dt
;
428 ret
= meson8b_init_rgmii_tx_clk(dwmac
);
430 goto err_remove_config_dt
;
432 ret
= dwmac
->data
->set_phy_mode(dwmac
);
434 goto err_remove_config_dt
;
436 ret
= meson8b_init_prg_eth(dwmac
);
438 goto err_remove_config_dt
;
440 plat_dat
->bsp_priv
= dwmac
;
442 ret
= stmmac_dvr_probe(&pdev
->dev
, plat_dat
, &stmmac_res
);
444 goto err_remove_config_dt
;
448 err_remove_config_dt
:
449 stmmac_remove_config_dt(pdev
, plat_dat
);
454 static const struct meson8b_dwmac_data meson8b_dwmac_data
= {
455 .set_phy_mode
= meson8b_set_phy_mode
,
458 static const struct meson8b_dwmac_data meson_axg_dwmac_data
= {
459 .set_phy_mode
= meson_axg_set_phy_mode
,
462 static const struct of_device_id meson8b_dwmac_match
[] = {
464 .compatible
= "amlogic,meson8b-dwmac",
465 .data
= &meson8b_dwmac_data
,
468 .compatible
= "amlogic,meson8m2-dwmac",
469 .data
= &meson8b_dwmac_data
,
472 .compatible
= "amlogic,meson-gxbb-dwmac",
473 .data
= &meson8b_dwmac_data
,
476 .compatible
= "amlogic,meson-axg-dwmac",
477 .data
= &meson_axg_dwmac_data
,
480 .compatible
= "amlogic,meson-g12a-dwmac",
481 .data
= &meson_axg_dwmac_data
,
485 MODULE_DEVICE_TABLE(of
, meson8b_dwmac_match
);
487 static struct platform_driver meson8b_dwmac_driver
= {
488 .probe
= meson8b_dwmac_probe
,
489 .remove
= stmmac_pltfr_remove
,
491 .name
= "meson8b-dwmac",
492 .pm
= &stmmac_pltfr_pm_ops
,
493 .of_match_table
= meson8b_dwmac_match
,
496 module_platform_driver(meson8b_dwmac_driver
);
498 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
499 MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
500 MODULE_LICENSE("GPL v2");