1 /*
2  * Amlogic Meson8b and GXBB DWMAC glue layer
3  *
4  * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * You should have received a copy of the GNU General Public License
11  * along with this program. If not, see <http://www.gnu.org/licenses/>.
12  */
13 
14 #include <linux/clk.h>
15 #include <linux/clk-provider.h>
16 #include <linux/device.h>
17 #include <linux/ethtool.h>
18 #include <linux/io.h>
19 #include <linux/ioport.h>
20 #include <linux/module.h>
21 #include <linux/of_net.h>
22 #include <linux/mfd/syscon.h>
23 #include <linux/platform_device.h>
24 #include <linux/stmmac.h>
25 
26 #include "stmmac_platform.h"
27 
28 #define PRG_ETH0			0x0
29 
30 #define PRG_ETH0_RGMII_MODE		BIT(0)
31 
32 /* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
33 #define PRG_ETH0_CLK_M250_SEL_SHIFT	4
34 #define PRG_ETH0_CLK_M250_SEL_MASK	GENMASK(4, 4)
35 
36 #define PRG_ETH0_TXDLY_SHIFT		5
37 #define PRG_ETH0_TXDLY_MASK		GENMASK(6, 5)
38 
39 /* divider for the result of m250_sel */
40 #define PRG_ETH0_CLK_M250_DIV_SHIFT	7
41 #define PRG_ETH0_CLK_M250_DIV_WIDTH	3
42 
43 #define PRG_ETH0_RGMII_TX_CLK_EN	10
44 
45 #define PRG_ETH0_INVERTED_RMII_CLK	BIT(11)
46 #define PRG_ETH0_TX_AND_PHY_REF_CLK	BIT(12)
47 
48 #define MUX_CLK_NUM_PARENTS		2
49 
50 struct meson8b_dwmac {
51 	struct platform_device	*pdev;
52 
53 	void __iomem		*regs;
54 
55 	phy_interface_t		phy_mode;
56 
57 	struct clk_mux		m250_mux;
58 	struct clk		*m250_mux_clk;
59 	struct clk		*m250_mux_parent[MUX_CLK_NUM_PARENTS];
60 
61 	struct clk_divider	m250_div;
62 	struct clk		*m250_div_clk;
63 
64 	struct clk_fixed_factor	fixed_div2;
65 	struct clk		*fixed_div2_clk;
66 
67 	struct clk_gate		rgmii_tx_en;
68 	struct clk		*rgmii_tx_en_clk;
69 
70 	u32			tx_delay_ns;
71 };
72 
73 static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
74 				    u32 mask, u32 value)
75 {
76 	u32 data;
77 
78 	data = readl(dwmac->regs + reg);
79 	data &= ~mask;
80 	data |= (value & mask);
81 
82 	writel(data, dwmac->regs + reg);
83 }
84 
85 static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
86 {
87 	struct clk_init_data init;
88 	int i, ret;
89 	struct device *dev = &dwmac->pdev->dev;
90 	char clk_name[32];
91 	const char *clk_div_parents[1];
92 	const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
93 
94 	/* get the mux parents from DT */
95 	for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
96 		char name[16];
97 
98 		snprintf(name, sizeof(name), "clkin%d", i);
99 		dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
100 		if (IS_ERR(dwmac->m250_mux_parent[i])) {
101 			ret = PTR_ERR(dwmac->m250_mux_parent[i]);
102 			if (ret != -EPROBE_DEFER)
103 				dev_err(dev, "Missing clock %s\n", name);
104 			return ret;
105 		}
106 
107 		mux_parent_names[i] =
108 			__clk_get_name(dwmac->m250_mux_parent[i]);
109 	}
110 
111 	/* create the m250_mux */
112 	snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
113 	init.name = clk_name;
114 	init.ops = &clk_mux_ops;
115 	init.flags = CLK_SET_RATE_PARENT;
116 	init.parent_names = mux_parent_names;
117 	init.num_parents = MUX_CLK_NUM_PARENTS;
118 
119 	dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
120 	dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
121 	dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
122 	dwmac->m250_mux.flags = 0;
123 	dwmac->m250_mux.table = NULL;
124 	dwmac->m250_mux.hw.init = &init;
125 
126 	dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
127 	if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
128 		return PTR_ERR(dwmac->m250_mux_clk);
129 
130 	/* create the m250_div */
131 	snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
132 	init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
133 	init.ops = &clk_divider_ops;
134 	init.flags = CLK_SET_RATE_PARENT;
135 	clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
136 	init.parent_names = clk_div_parents;
137 	init.num_parents = ARRAY_SIZE(clk_div_parents);
138 
139 	dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
140 	dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
141 	dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
142 	dwmac->m250_div.hw.init = &init;
143 	dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED |
144 				CLK_DIVIDER_ALLOW_ZERO |
145 				CLK_DIVIDER_ROUND_CLOSEST;
146 
147 	dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
148 	if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
149 		return PTR_ERR(dwmac->m250_div_clk);
150 
151 	/* create the fixed_div2 */
152 	snprintf(clk_name, sizeof(clk_name), "%s#fixed_div2", dev_name(dev));
153 	init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
154 	init.ops = &clk_fixed_factor_ops;
155 	init.flags = CLK_SET_RATE_PARENT;
156 	clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
157 	init.parent_names = clk_div_parents;
158 	init.num_parents = ARRAY_SIZE(clk_div_parents);
159 
160 	dwmac->fixed_div2.mult = 1;
161 	dwmac->fixed_div2.div = 2;
162 	dwmac->fixed_div2.hw.init = &init;
163 
164 	dwmac->fixed_div2_clk = devm_clk_register(dev, &dwmac->fixed_div2.hw);
165 	if (WARN_ON(IS_ERR(dwmac->fixed_div2_clk)))
166 		return PTR_ERR(dwmac->fixed_div2_clk);
167 
168 	/* create the rgmii_tx_en */
169 	init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#rgmii_tx_en",
170 				   dev_name(dev));
171 	init.ops = &clk_gate_ops;
172 	init.flags = CLK_SET_RATE_PARENT;
173 	clk_div_parents[0] = __clk_get_name(dwmac->fixed_div2_clk);
174 	init.parent_names = clk_div_parents;
175 	init.num_parents = ARRAY_SIZE(clk_div_parents);
176 
177 	dwmac->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
178 	dwmac->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
179 	dwmac->rgmii_tx_en.hw.init = &init;
180 
181 	dwmac->rgmii_tx_en_clk = devm_clk_register(dev,
182 						   &dwmac->rgmii_tx_en.hw);
183 	if (WARN_ON(IS_ERR(dwmac->rgmii_tx_en_clk)))
184 		return PTR_ERR(dwmac->rgmii_tx_en_clk);
185 
186 	return 0;
187 }
188 
189 static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
190 {
191 	int ret;
192 	u8 tx_dly_val = 0;
193 
194 	switch (dwmac->phy_mode) {
195 	case PHY_INTERFACE_MODE_RGMII:
196 	case PHY_INTERFACE_MODE_RGMII_RXID:
197 		/* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where
198 		 * 8ns are exactly one cycle of the 125MHz RGMII TX clock):
199 		 * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
200 		 */
201 		tx_dly_val = dwmac->tx_delay_ns >> 1;
202 		/* fall through */
203 
204 	case PHY_INTERFACE_MODE_RGMII_ID:
205 	case PHY_INTERFACE_MODE_RGMII_TXID:
206 		/* enable RGMII mode */
207 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
208 					PRG_ETH0_RGMII_MODE);
209 
210 		/* only relevant for RMII mode -> disable in RGMII mode */
211 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
212 					PRG_ETH0_INVERTED_RMII_CLK, 0);
213 
214 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
215 					tx_dly_val << PRG_ETH0_TXDLY_SHIFT);
216 
217 		/* Configure the 125MHz RGMII TX clock, the IP block changes
218 		 * the output automatically (= without us having to configure
219 		 * a register) based on the line-speed (125MHz for Gbit speeds,
220 		 * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
221 		 */
222 		ret = clk_set_rate(dwmac->rgmii_tx_en_clk, 125 * 1000 * 1000);
223 		if (ret) {
224 			dev_err(&dwmac->pdev->dev,
225 				"failed to set RGMII TX clock\n");
226 			return ret;
227 		}
228 
229 		ret = clk_prepare_enable(dwmac->rgmii_tx_en_clk);
230 		if (ret) {
231 			dev_err(&dwmac->pdev->dev,
232 				"failed to enable the RGMII TX clock\n");
233 			return ret;
234 		}
235 		break;
236 
237 	case PHY_INTERFACE_MODE_RMII:
238 		/* disable RGMII mode -> enables RMII mode */
239 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
240 					0);
241 
242 		/* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
243 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
244 					PRG_ETH0_INVERTED_RMII_CLK,
245 					PRG_ETH0_INVERTED_RMII_CLK);
246 
247 		/* TX clock delay cannot be configured in RMII mode */
248 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
249 					0);
250 
251 		break;
252 
253 	default:
254 		dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
255 			phy_modes(dwmac->phy_mode));
256 		return -EINVAL;
257 	}
258 
259 	/* enable TX_CLK and PHY_REF_CLK generator */
260 	meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
261 				PRG_ETH0_TX_AND_PHY_REF_CLK);
262 
263 	return 0;
264 }
265 
266 static int meson8b_dwmac_probe(struct platform_device *pdev)
267 {
268 	struct plat_stmmacenet_data *plat_dat;
269 	struct stmmac_resources stmmac_res;
270 	struct resource *res;
271 	struct meson8b_dwmac *dwmac;
272 	int ret;
273 
274 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
275 	if (ret)
276 		return ret;
277 
278 	plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
279 	if (IS_ERR(plat_dat))
280 		return PTR_ERR(plat_dat);
281 
282 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
283 	if (!dwmac) {
284 		ret = -ENOMEM;
285 		goto err_remove_config_dt;
286 	}
287 
288 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
289 	dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
290 	if (IS_ERR(dwmac->regs)) {
291 		ret = PTR_ERR(dwmac->regs);
292 		goto err_remove_config_dt;
293 	}
294 
295 	dwmac->pdev = pdev;
296 	dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
297 	if (dwmac->phy_mode < 0) {
298 		dev_err(&pdev->dev, "missing phy-mode property\n");
299 		ret = -EINVAL;
300 		goto err_remove_config_dt;
301 	}
302 
303 	/* use 2ns as fallback since this value was previously hardcoded */
304 	if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
305 				 &dwmac->tx_delay_ns))
306 		dwmac->tx_delay_ns = 2;
307 
308 	ret = meson8b_init_rgmii_tx_clk(dwmac);
309 	if (ret)
310 		goto err_remove_config_dt;
311 
312 	ret = meson8b_init_prg_eth(dwmac);
313 	if (ret)
314 		goto err_remove_config_dt;
315 
316 	plat_dat->bsp_priv = dwmac;
317 
318 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
319 	if (ret)
320 		goto err_clk_disable;
321 
322 	return 0;
323 
324 err_clk_disable:
325 	if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
326 		clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
327 err_remove_config_dt:
328 	stmmac_remove_config_dt(pdev, plat_dat);
329 
330 	return ret;
331 }
332 
333 static int meson8b_dwmac_remove(struct platform_device *pdev)
334 {
335 	struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
336 
337 	if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
338 		clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
339 
340 	return stmmac_pltfr_remove(pdev);
341 }
342 
343 static const struct of_device_id meson8b_dwmac_match[] = {
344 	{ .compatible = "amlogic,meson8b-dwmac" },
345 	{ .compatible = "amlogic,meson-gxbb-dwmac" },
346 	{ }
347 };
348 MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
349 
350 static struct platform_driver meson8b_dwmac_driver = {
351 	.probe  = meson8b_dwmac_probe,
352 	.remove = meson8b_dwmac_remove,
353 	.driver = {
354 		.name           = "meson8b-dwmac",
355 		.pm		= &stmmac_pltfr_pm_ops,
356 		.of_match_table = meson8b_dwmac_match,
357 	},
358 };
359 module_platform_driver(meson8b_dwmac_driver);
360 
361 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
362 MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
363 MODULE_LICENSE("GPL v2");
364