1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/bitfield.h>
9*4882a593Smuzhiyun #include <linux/clk.h>
10*4882a593Smuzhiyun #include <linux/clk-provider.h>
11*4882a593Smuzhiyun #include <linux/device.h>
12*4882a593Smuzhiyun #include <linux/ethtool.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/ioport.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/of_device.h>
17*4882a593Smuzhiyun #include <linux/of_net.h>
18*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
19*4882a593Smuzhiyun #include <linux/platform_device.h>
20*4882a593Smuzhiyun #include <linux/stmmac.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include "stmmac_platform.h"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define PRG_ETH0 0x0
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define PRG_ETH0_RGMII_MODE BIT(0)
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define PRG_ETH0_EXT_PHY_MODE_MASK GENMASK(2, 0)
29*4882a593Smuzhiyun #define PRG_ETH0_EXT_RGMII_MODE 1
30*4882a593Smuzhiyun #define PRG_ETH0_EXT_RMII_MODE 4
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun /* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
33*4882a593Smuzhiyun #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where 8ns are exactly one
36*4882a593Smuzhiyun * cycle of the 125MHz RGMII TX clock):
37*4882a593Smuzhiyun * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun /* divider for the result of m250_sel */
42*4882a593Smuzhiyun #define PRG_ETH0_CLK_M250_DIV_SHIFT 7
43*4882a593Smuzhiyun #define PRG_ETH0_CLK_M250_DIV_WIDTH 3
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #define PRG_ETH0_RGMII_TX_CLK_EN 10
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define PRG_ETH0_INVERTED_RMII_CLK BIT(11)
48*4882a593Smuzhiyun #define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12)
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /* Bypass (= 0, the signal from the GPIO input directly connects to the
51*4882a593Smuzhiyun * internal sampling) or enable (= 1) the internal logic for RXEN and RXD[3:0]
52*4882a593Smuzhiyun * timing tuning.
53*4882a593Smuzhiyun */
54*4882a593Smuzhiyun #define PRG_ETH0_ADJ_ENABLE BIT(13)
55*4882a593Smuzhiyun /* Controls whether the RXEN and RXD[3:0] signals should be aligned with the
56*4882a593Smuzhiyun * input RX rising/falling edge and sent to the Ethernet internals. This sets
57*4882a593Smuzhiyun * the automatically delay and skew automatically (internally).
58*4882a593Smuzhiyun */
59*4882a593Smuzhiyun #define PRG_ETH0_ADJ_SETUP BIT(14)
60*4882a593Smuzhiyun /* An internal counter based on the "timing-adjustment" clock. The counter is
61*4882a593Smuzhiyun * cleared on both, the falling and rising edge of the RX_CLK. This selects the
62*4882a593Smuzhiyun * delay (= the counter value) when to start sampling RXEN and RXD[3:0].
63*4882a593Smuzhiyun */
64*4882a593Smuzhiyun #define PRG_ETH0_ADJ_DELAY GENMASK(19, 15)
65*4882a593Smuzhiyun /* Adjusts the skew between each bit of RXEN and RXD[3:0]. If a signal has a
66*4882a593Smuzhiyun * large input delay, the bit for that signal (RXEN = bit 0, RXD[3] = bit 1,
67*4882a593Smuzhiyun * ...) can be configured to be 1 to compensate for a delay of about 1ns.
68*4882a593Smuzhiyun */
69*4882a593Smuzhiyun #define PRG_ETH0_ADJ_SKEW GENMASK(24, 20)
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun struct meson8b_dwmac;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct meson8b_dwmac_data {
74*4882a593Smuzhiyun int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun struct meson8b_dwmac {
78*4882a593Smuzhiyun struct device *dev;
79*4882a593Smuzhiyun void __iomem *regs;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun const struct meson8b_dwmac_data *data;
82*4882a593Smuzhiyun phy_interface_t phy_mode;
83*4882a593Smuzhiyun struct clk *rgmii_tx_clk;
84*4882a593Smuzhiyun u32 tx_delay_ns;
85*4882a593Smuzhiyun u32 rx_delay_ns;
86*4882a593Smuzhiyun struct clk *timing_adj_clk;
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun struct meson8b_dwmac_clk_configs {
90*4882a593Smuzhiyun struct clk_mux m250_mux;
91*4882a593Smuzhiyun struct clk_divider m250_div;
92*4882a593Smuzhiyun struct clk_fixed_factor fixed_div2;
93*4882a593Smuzhiyun struct clk_gate rgmii_tx_en;
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun
meson8b_dwmac_mask_bits(struct meson8b_dwmac * dwmac,u32 reg,u32 mask,u32 value)96*4882a593Smuzhiyun static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
97*4882a593Smuzhiyun u32 mask, u32 value)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun u32 data;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun data = readl(dwmac->regs + reg);
102*4882a593Smuzhiyun data &= ~mask;
103*4882a593Smuzhiyun data |= (value & mask);
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun writel(data, dwmac->regs + reg);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
meson8b_dwmac_register_clk(struct meson8b_dwmac * dwmac,const char * name_suffix,const struct clk_parent_data * parents,int num_parents,const struct clk_ops * ops,struct clk_hw * hw)108*4882a593Smuzhiyun static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac,
109*4882a593Smuzhiyun const char *name_suffix,
110*4882a593Smuzhiyun const struct clk_parent_data *parents,
111*4882a593Smuzhiyun int num_parents,
112*4882a593Smuzhiyun const struct clk_ops *ops,
113*4882a593Smuzhiyun struct clk_hw *hw)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun struct clk_init_data init = { };
116*4882a593Smuzhiyun char clk_name[32];
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dwmac->dev),
119*4882a593Smuzhiyun name_suffix);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun init.name = clk_name;
122*4882a593Smuzhiyun init.ops = ops;
123*4882a593Smuzhiyun init.flags = CLK_SET_RATE_PARENT;
124*4882a593Smuzhiyun init.parent_data = parents;
125*4882a593Smuzhiyun init.num_parents = num_parents;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun hw->init = &init;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun return devm_clk_register(dwmac->dev, hw);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
meson8b_init_rgmii_tx_clk(struct meson8b_dwmac * dwmac)132*4882a593Smuzhiyun static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun struct clk *clk;
135*4882a593Smuzhiyun struct device *dev = dwmac->dev;
136*4882a593Smuzhiyun static const struct clk_parent_data mux_parents[] = {
137*4882a593Smuzhiyun { .fw_name = "clkin0", },
138*4882a593Smuzhiyun { .index = -1, },
139*4882a593Smuzhiyun };
140*4882a593Smuzhiyun static const struct clk_div_table div_table[] = {
141*4882a593Smuzhiyun { .div = 2, .val = 2, },
142*4882a593Smuzhiyun { .div = 3, .val = 3, },
143*4882a593Smuzhiyun { .div = 4, .val = 4, },
144*4882a593Smuzhiyun { .div = 5, .val = 5, },
145*4882a593Smuzhiyun { .div = 6, .val = 6, },
146*4882a593Smuzhiyun { .div = 7, .val = 7, },
147*4882a593Smuzhiyun { /* end of array */ }
148*4882a593Smuzhiyun };
149*4882a593Smuzhiyun struct meson8b_dwmac_clk_configs *clk_configs;
150*4882a593Smuzhiyun struct clk_parent_data parent_data = { };
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
153*4882a593Smuzhiyun if (!clk_configs)
154*4882a593Smuzhiyun return -ENOMEM;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0;
157*4882a593Smuzhiyun clk_configs->m250_mux.shift = __ffs(PRG_ETH0_CLK_M250_SEL_MASK);
158*4882a593Smuzhiyun clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK >>
159*4882a593Smuzhiyun clk_configs->m250_mux.shift;
160*4882a593Smuzhiyun clk = meson8b_dwmac_register_clk(dwmac, "m250_sel", mux_parents,
161*4882a593Smuzhiyun ARRAY_SIZE(mux_parents), &clk_mux_ops,
162*4882a593Smuzhiyun &clk_configs->m250_mux.hw);
163*4882a593Smuzhiyun if (WARN_ON(IS_ERR(clk)))
164*4882a593Smuzhiyun return PTR_ERR(clk);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun parent_data.hw = &clk_configs->m250_mux.hw;
167*4882a593Smuzhiyun clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0;
168*4882a593Smuzhiyun clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
169*4882a593Smuzhiyun clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
170*4882a593Smuzhiyun clk_configs->m250_div.table = div_table;
171*4882a593Smuzhiyun clk_configs->m250_div.flags = CLK_DIVIDER_ALLOW_ZERO |
172*4882a593Smuzhiyun CLK_DIVIDER_ROUND_CLOSEST;
173*4882a593Smuzhiyun clk = meson8b_dwmac_register_clk(dwmac, "m250_div", &parent_data, 1,
174*4882a593Smuzhiyun &clk_divider_ops,
175*4882a593Smuzhiyun &clk_configs->m250_div.hw);
176*4882a593Smuzhiyun if (WARN_ON(IS_ERR(clk)))
177*4882a593Smuzhiyun return PTR_ERR(clk);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun parent_data.hw = &clk_configs->m250_div.hw;
180*4882a593Smuzhiyun clk_configs->fixed_div2.mult = 1;
181*4882a593Smuzhiyun clk_configs->fixed_div2.div = 2;
182*4882a593Smuzhiyun clk = meson8b_dwmac_register_clk(dwmac, "fixed_div2", &parent_data, 1,
183*4882a593Smuzhiyun &clk_fixed_factor_ops,
184*4882a593Smuzhiyun &clk_configs->fixed_div2.hw);
185*4882a593Smuzhiyun if (WARN_ON(IS_ERR(clk)))
186*4882a593Smuzhiyun return PTR_ERR(clk);
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun parent_data.hw = &clk_configs->fixed_div2.hw;
189*4882a593Smuzhiyun clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
190*4882a593Smuzhiyun clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
191*4882a593Smuzhiyun clk = meson8b_dwmac_register_clk(dwmac, "rgmii_tx_en", &parent_data, 1,
192*4882a593Smuzhiyun &clk_gate_ops,
193*4882a593Smuzhiyun &clk_configs->rgmii_tx_en.hw);
194*4882a593Smuzhiyun if (WARN_ON(IS_ERR(clk)))
195*4882a593Smuzhiyun return PTR_ERR(clk);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun dwmac->rgmii_tx_clk = clk;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun return 0;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
meson8b_set_phy_mode(struct meson8b_dwmac * dwmac)202*4882a593Smuzhiyun static int meson8b_set_phy_mode(struct meson8b_dwmac *dwmac)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun switch (dwmac->phy_mode) {
205*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII:
206*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_RXID:
207*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_ID:
208*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_TXID:
209*4882a593Smuzhiyun /* enable RGMII mode */
210*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
211*4882a593Smuzhiyun PRG_ETH0_RGMII_MODE,
212*4882a593Smuzhiyun PRG_ETH0_RGMII_MODE);
213*4882a593Smuzhiyun break;
214*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RMII:
215*4882a593Smuzhiyun /* disable RGMII mode -> enables RMII mode */
216*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
217*4882a593Smuzhiyun PRG_ETH0_RGMII_MODE, 0);
218*4882a593Smuzhiyun break;
219*4882a593Smuzhiyun default:
220*4882a593Smuzhiyun dev_err(dwmac->dev, "fail to set phy-mode %s\n",
221*4882a593Smuzhiyun phy_modes(dwmac->phy_mode));
222*4882a593Smuzhiyun return -EINVAL;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun return 0;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
meson_axg_set_phy_mode(struct meson8b_dwmac * dwmac)228*4882a593Smuzhiyun static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun switch (dwmac->phy_mode) {
231*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII:
232*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_RXID:
233*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_ID:
234*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_TXID:
235*4882a593Smuzhiyun /* enable RGMII mode */
236*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
237*4882a593Smuzhiyun PRG_ETH0_EXT_PHY_MODE_MASK,
238*4882a593Smuzhiyun PRG_ETH0_EXT_RGMII_MODE);
239*4882a593Smuzhiyun break;
240*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RMII:
241*4882a593Smuzhiyun /* disable RGMII mode -> enables RMII mode */
242*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
243*4882a593Smuzhiyun PRG_ETH0_EXT_PHY_MODE_MASK,
244*4882a593Smuzhiyun PRG_ETH0_EXT_RMII_MODE);
245*4882a593Smuzhiyun break;
246*4882a593Smuzhiyun default:
247*4882a593Smuzhiyun dev_err(dwmac->dev, "fail to set phy-mode %s\n",
248*4882a593Smuzhiyun phy_modes(dwmac->phy_mode));
249*4882a593Smuzhiyun return -EINVAL;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun return 0;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun
meson8b_devm_clk_prepare_enable(struct meson8b_dwmac * dwmac,struct clk * clk)255*4882a593Smuzhiyun static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
256*4882a593Smuzhiyun struct clk *clk)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun int ret;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun ret = clk_prepare_enable(clk);
261*4882a593Smuzhiyun if (ret)
262*4882a593Smuzhiyun return ret;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun return devm_add_action_or_reset(dwmac->dev,
265*4882a593Smuzhiyun (void(*)(void *))clk_disable_unprepare,
266*4882a593Smuzhiyun clk);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
meson8b_init_prg_eth(struct meson8b_dwmac * dwmac)269*4882a593Smuzhiyun static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun u32 tx_dly_config, rx_dly_config, delay_config;
272*4882a593Smuzhiyun int ret;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
275*4882a593Smuzhiyun dwmac->tx_delay_ns >> 1);
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun if (dwmac->rx_delay_ns == 2)
278*4882a593Smuzhiyun rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
279*4882a593Smuzhiyun else
280*4882a593Smuzhiyun rx_dly_config = 0;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun switch (dwmac->phy_mode) {
283*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII:
284*4882a593Smuzhiyun delay_config = tx_dly_config | rx_dly_config;
285*4882a593Smuzhiyun break;
286*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_RXID:
287*4882a593Smuzhiyun delay_config = tx_dly_config;
288*4882a593Smuzhiyun break;
289*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_TXID:
290*4882a593Smuzhiyun delay_config = rx_dly_config;
291*4882a593Smuzhiyun break;
292*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RGMII_ID:
293*4882a593Smuzhiyun case PHY_INTERFACE_MODE_RMII:
294*4882a593Smuzhiyun delay_config = 0;
295*4882a593Smuzhiyun break;
296*4882a593Smuzhiyun default:
297*4882a593Smuzhiyun dev_err(dwmac->dev, "unsupported phy-mode %s\n",
298*4882a593Smuzhiyun phy_modes(dwmac->phy_mode));
299*4882a593Smuzhiyun return -EINVAL;
300*4882a593Smuzhiyun };
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun if (delay_config & PRG_ETH0_ADJ_ENABLE) {
303*4882a593Smuzhiyun if (!dwmac->timing_adj_clk) {
304*4882a593Smuzhiyun dev_err(dwmac->dev,
305*4882a593Smuzhiyun "The timing-adjustment clock is mandatory for the RX delay re-timing\n");
306*4882a593Smuzhiyun return -EINVAL;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun /* The timing adjustment logic is driven by a separate clock */
310*4882a593Smuzhiyun ret = meson8b_devm_clk_prepare_enable(dwmac,
311*4882a593Smuzhiyun dwmac->timing_adj_clk);
312*4882a593Smuzhiyun if (ret) {
313*4882a593Smuzhiyun dev_err(dwmac->dev,
314*4882a593Smuzhiyun "Failed to enable the timing-adjustment clock\n");
315*4882a593Smuzhiyun return ret;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK |
320*4882a593Smuzhiyun PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP |
321*4882a593Smuzhiyun PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
322*4882a593Smuzhiyun delay_config);
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) {
325*4882a593Smuzhiyun /* only relevant for RMII mode -> disable in RGMII mode */
326*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
327*4882a593Smuzhiyun PRG_ETH0_INVERTED_RMII_CLK, 0);
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun /* Configure the 125MHz RGMII TX clock, the IP block changes
330*4882a593Smuzhiyun * the output automatically (= without us having to configure
331*4882a593Smuzhiyun * a register) based on the line-speed (125MHz for Gbit speeds,
332*4882a593Smuzhiyun * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
333*4882a593Smuzhiyun */
334*4882a593Smuzhiyun ret = clk_set_rate(dwmac->rgmii_tx_clk, 125 * 1000 * 1000);
335*4882a593Smuzhiyun if (ret) {
336*4882a593Smuzhiyun dev_err(dwmac->dev,
337*4882a593Smuzhiyun "failed to set RGMII TX clock\n");
338*4882a593Smuzhiyun return ret;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun ret = meson8b_devm_clk_prepare_enable(dwmac,
342*4882a593Smuzhiyun dwmac->rgmii_tx_clk);
343*4882a593Smuzhiyun if (ret) {
344*4882a593Smuzhiyun dev_err(dwmac->dev,
345*4882a593Smuzhiyun "failed to enable the RGMII TX clock\n");
346*4882a593Smuzhiyun return ret;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun } else {
349*4882a593Smuzhiyun /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
350*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
351*4882a593Smuzhiyun PRG_ETH0_INVERTED_RMII_CLK,
352*4882a593Smuzhiyun PRG_ETH0_INVERTED_RMII_CLK);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun /* enable TX_CLK and PHY_REF_CLK generator */
356*4882a593Smuzhiyun meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
357*4882a593Smuzhiyun PRG_ETH0_TX_AND_PHY_REF_CLK);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun return 0;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun
meson8b_dwmac_probe(struct platform_device * pdev)362*4882a593Smuzhiyun static int meson8b_dwmac_probe(struct platform_device *pdev)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun struct plat_stmmacenet_data *plat_dat;
365*4882a593Smuzhiyun struct stmmac_resources stmmac_res;
366*4882a593Smuzhiyun struct meson8b_dwmac *dwmac;
367*4882a593Smuzhiyun int ret;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun ret = stmmac_get_platform_resources(pdev, &stmmac_res);
370*4882a593Smuzhiyun if (ret)
371*4882a593Smuzhiyun return ret;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
374*4882a593Smuzhiyun if (IS_ERR(plat_dat))
375*4882a593Smuzhiyun return PTR_ERR(plat_dat);
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
378*4882a593Smuzhiyun if (!dwmac) {
379*4882a593Smuzhiyun ret = -ENOMEM;
380*4882a593Smuzhiyun goto err_remove_config_dt;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun dwmac->data = (const struct meson8b_dwmac_data *)
384*4882a593Smuzhiyun of_device_get_match_data(&pdev->dev);
385*4882a593Smuzhiyun if (!dwmac->data) {
386*4882a593Smuzhiyun ret = -EINVAL;
387*4882a593Smuzhiyun goto err_remove_config_dt;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun dwmac->regs = devm_platform_ioremap_resource(pdev, 1);
390*4882a593Smuzhiyun if (IS_ERR(dwmac->regs)) {
391*4882a593Smuzhiyun ret = PTR_ERR(dwmac->regs);
392*4882a593Smuzhiyun goto err_remove_config_dt;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun dwmac->dev = &pdev->dev;
396*4882a593Smuzhiyun ret = of_get_phy_mode(pdev->dev.of_node, &dwmac->phy_mode);
397*4882a593Smuzhiyun if (ret) {
398*4882a593Smuzhiyun dev_err(&pdev->dev, "missing phy-mode property\n");
399*4882a593Smuzhiyun goto err_remove_config_dt;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun /* use 2ns as fallback since this value was previously hardcoded */
403*4882a593Smuzhiyun if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
404*4882a593Smuzhiyun &dwmac->tx_delay_ns))
405*4882a593Smuzhiyun dwmac->tx_delay_ns = 2;
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun /* use 0ns as fallback since this is what most boards actually use */
408*4882a593Smuzhiyun if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns",
409*4882a593Smuzhiyun &dwmac->rx_delay_ns))
410*4882a593Smuzhiyun dwmac->rx_delay_ns = 0;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun if (dwmac->rx_delay_ns != 0 && dwmac->rx_delay_ns != 2) {
413*4882a593Smuzhiyun dev_err(&pdev->dev,
414*4882a593Smuzhiyun "The only allowed RX delays values are: 0ns, 2ns");
415*4882a593Smuzhiyun ret = -EINVAL;
416*4882a593Smuzhiyun goto err_remove_config_dt;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
420*4882a593Smuzhiyun "timing-adjustment");
421*4882a593Smuzhiyun if (IS_ERR(dwmac->timing_adj_clk)) {
422*4882a593Smuzhiyun ret = PTR_ERR(dwmac->timing_adj_clk);
423*4882a593Smuzhiyun goto err_remove_config_dt;
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun ret = meson8b_init_rgmii_tx_clk(dwmac);
427*4882a593Smuzhiyun if (ret)
428*4882a593Smuzhiyun goto err_remove_config_dt;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun ret = dwmac->data->set_phy_mode(dwmac);
431*4882a593Smuzhiyun if (ret)
432*4882a593Smuzhiyun goto err_remove_config_dt;
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun ret = meson8b_init_prg_eth(dwmac);
435*4882a593Smuzhiyun if (ret)
436*4882a593Smuzhiyun goto err_remove_config_dt;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun plat_dat->bsp_priv = dwmac;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
441*4882a593Smuzhiyun if (ret)
442*4882a593Smuzhiyun goto err_remove_config_dt;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun return 0;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun err_remove_config_dt:
447*4882a593Smuzhiyun stmmac_remove_config_dt(pdev, plat_dat);
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun return ret;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun static const struct meson8b_dwmac_data meson8b_dwmac_data = {
453*4882a593Smuzhiyun .set_phy_mode = meson8b_set_phy_mode,
454*4882a593Smuzhiyun };
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
457*4882a593Smuzhiyun .set_phy_mode = meson_axg_set_phy_mode,
458*4882a593Smuzhiyun };
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun static const struct of_device_id meson8b_dwmac_match[] = {
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun .compatible = "amlogic,meson8b-dwmac",
463*4882a593Smuzhiyun .data = &meson8b_dwmac_data,
464*4882a593Smuzhiyun },
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun .compatible = "amlogic,meson8m2-dwmac",
467*4882a593Smuzhiyun .data = &meson8b_dwmac_data,
468*4882a593Smuzhiyun },
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun .compatible = "amlogic,meson-gxbb-dwmac",
471*4882a593Smuzhiyun .data = &meson8b_dwmac_data,
472*4882a593Smuzhiyun },
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun .compatible = "amlogic,meson-axg-dwmac",
475*4882a593Smuzhiyun .data = &meson_axg_dwmac_data,
476*4882a593Smuzhiyun },
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun .compatible = "amlogic,meson-g12a-dwmac",
479*4882a593Smuzhiyun .data = &meson_axg_dwmac_data,
480*4882a593Smuzhiyun },
481*4882a593Smuzhiyun { }
482*4882a593Smuzhiyun };
483*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun static struct platform_driver meson8b_dwmac_driver = {
486*4882a593Smuzhiyun .probe = meson8b_dwmac_probe,
487*4882a593Smuzhiyun .remove = stmmac_pltfr_remove,
488*4882a593Smuzhiyun .driver = {
489*4882a593Smuzhiyun .name = "meson8b-dwmac",
490*4882a593Smuzhiyun .pm = &stmmac_pltfr_pm_ops,
491*4882a593Smuzhiyun .of_match_table = meson8b_dwmac_match,
492*4882a593Smuzhiyun },
493*4882a593Smuzhiyun };
494*4882a593Smuzhiyun module_platform_driver(meson8b_dwmac_driver);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
497*4882a593Smuzhiyun MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer");
498*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
499