xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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