xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c (revision e5c86679d5e864947a52fb31e45a425dea3e7fa9)
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 /* divides the result of m25_sel by either 5 (bit unset) or 10 (bit set) */
44 #define PRG_ETH0_CLK_M25_DIV_SHIFT	10
45 #define PRG_ETH0_CLK_M25_DIV_WIDTH	1
46 
47 #define PRG_ETH0_INVERTED_RMII_CLK	BIT(11)
48 #define PRG_ETH0_TX_AND_PHY_REF_CLK	BIT(12)
49 
50 #define MUX_CLK_NUM_PARENTS		2
51 
52 struct meson8b_dwmac {
53 	struct platform_device	*pdev;
54 
55 	void __iomem		*regs;
56 
57 	phy_interface_t		phy_mode;
58 
59 	struct clk_mux		m250_mux;
60 	struct clk		*m250_mux_clk;
61 	struct clk		*m250_mux_parent[MUX_CLK_NUM_PARENTS];
62 
63 	struct clk_divider	m250_div;
64 	struct clk		*m250_div_clk;
65 
66 	struct clk_divider	m25_div;
67 	struct clk		*m25_div_clk;
68 
69 	u32			tx_delay_ns;
70 };
71 
72 static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
73 				    u32 mask, u32 value)
74 {
75 	u32 data;
76 
77 	data = readl(dwmac->regs + reg);
78 	data &= ~mask;
79 	data |= (value & mask);
80 
81 	writel(data, dwmac->regs + reg);
82 }
83 
84 static int meson8b_init_clk(struct meson8b_dwmac *dwmac)
85 {
86 	struct clk_init_data init;
87 	int i, ret;
88 	struct device *dev = &dwmac->pdev->dev;
89 	char clk_name[32];
90 	const char *clk_div_parents[1];
91 	const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
92 	static struct clk_div_table clk_25m_div_table[] = {
93 		{ .val = 0, .div = 5 },
94 		{ .val = 1, .div = 10 },
95 		{ /* sentinel */ },
96 	};
97 
98 	/* get the mux parents from DT */
99 	for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
100 		char name[16];
101 
102 		snprintf(name, sizeof(name), "clkin%d", i);
103 		dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
104 		if (IS_ERR(dwmac->m250_mux_parent[i])) {
105 			ret = PTR_ERR(dwmac->m250_mux_parent[i]);
106 			if (ret != -EPROBE_DEFER)
107 				dev_err(dev, "Missing clock %s\n", name);
108 			return ret;
109 		}
110 
111 		mux_parent_names[i] =
112 			__clk_get_name(dwmac->m250_mux_parent[i]);
113 	}
114 
115 	/* create the m250_mux */
116 	snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
117 	init.name = clk_name;
118 	init.ops = &clk_mux_ops;
119 	init.flags = 0;
120 	init.parent_names = mux_parent_names;
121 	init.num_parents = MUX_CLK_NUM_PARENTS;
122 
123 	dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
124 	dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
125 	dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
126 	dwmac->m250_mux.flags = 0;
127 	dwmac->m250_mux.table = NULL;
128 	dwmac->m250_mux.hw.init = &init;
129 
130 	dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
131 	if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
132 		return PTR_ERR(dwmac->m250_mux_clk);
133 
134 	/* create the m250_div */
135 	snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
136 	init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
137 	init.ops = &clk_divider_ops;
138 	init.flags = CLK_SET_RATE_PARENT;
139 	clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
140 	init.parent_names = clk_div_parents;
141 	init.num_parents = ARRAY_SIZE(clk_div_parents);
142 
143 	dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
144 	dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
145 	dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
146 	dwmac->m250_div.hw.init = &init;
147 	dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO;
148 
149 	dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
150 	if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
151 		return PTR_ERR(dwmac->m250_div_clk);
152 
153 	/* create the m25_div */
154 	snprintf(clk_name, sizeof(clk_name), "%s#m25_div", dev_name(dev));
155 	init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
156 	init.ops = &clk_divider_ops;
157 	init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
158 	clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
159 	init.parent_names = clk_div_parents;
160 	init.num_parents = ARRAY_SIZE(clk_div_parents);
161 
162 	dwmac->m25_div.reg = dwmac->regs + PRG_ETH0;
163 	dwmac->m25_div.shift = PRG_ETH0_CLK_M25_DIV_SHIFT;
164 	dwmac->m25_div.width = PRG_ETH0_CLK_M25_DIV_WIDTH;
165 	dwmac->m25_div.table = clk_25m_div_table;
166 	dwmac->m25_div.hw.init = &init;
167 	dwmac->m25_div.flags = CLK_DIVIDER_ALLOW_ZERO;
168 
169 	dwmac->m25_div_clk = devm_clk_register(dev, &dwmac->m25_div.hw);
170 	if (WARN_ON(IS_ERR(dwmac->m25_div_clk)))
171 		return PTR_ERR(dwmac->m25_div_clk);
172 
173 	return 0;
174 }
175 
176 static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
177 {
178 	int ret;
179 	unsigned long clk_rate;
180 	u8 tx_dly_val = 0;
181 
182 	switch (dwmac->phy_mode) {
183 	case PHY_INTERFACE_MODE_RGMII:
184 	case PHY_INTERFACE_MODE_RGMII_RXID:
185 		/* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where
186 		 * 8ns are exactly one cycle of the 125MHz RGMII TX clock):
187 		 * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
188 		 */
189 		tx_dly_val = dwmac->tx_delay_ns >> 1;
190 		/* fall through */
191 
192 	case PHY_INTERFACE_MODE_RGMII_ID:
193 	case PHY_INTERFACE_MODE_RGMII_TXID:
194 		/* Generate a 25MHz clock for the PHY */
195 		clk_rate = 25 * 1000 * 1000;
196 
197 		/* enable RGMII mode */
198 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
199 					PRG_ETH0_RGMII_MODE);
200 
201 		/* only relevant for RMII mode -> disable in RGMII mode */
202 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
203 					PRG_ETH0_INVERTED_RMII_CLK, 0);
204 
205 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
206 					tx_dly_val << PRG_ETH0_TXDLY_SHIFT);
207 		break;
208 
209 	case PHY_INTERFACE_MODE_RMII:
210 		/* Use the rate of the mux clock for the internal RMII PHY */
211 		clk_rate = clk_get_rate(dwmac->m250_mux_clk);
212 
213 		/* disable RGMII mode -> enables RMII mode */
214 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
215 					0);
216 
217 		/* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
218 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
219 					PRG_ETH0_INVERTED_RMII_CLK,
220 					PRG_ETH0_INVERTED_RMII_CLK);
221 
222 		/* TX clock delay cannot be configured in RMII mode */
223 		meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
224 					0);
225 
226 		break;
227 
228 	default:
229 		dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
230 			phy_modes(dwmac->phy_mode));
231 		return -EINVAL;
232 	}
233 
234 	ret = clk_prepare_enable(dwmac->m25_div_clk);
235 	if (ret) {
236 		dev_err(&dwmac->pdev->dev, "failed to enable the PHY clock\n");
237 		return ret;
238 	}
239 
240 	ret = clk_set_rate(dwmac->m25_div_clk, clk_rate);
241 	if (ret) {
242 		clk_disable_unprepare(dwmac->m25_div_clk);
243 
244 		dev_err(&dwmac->pdev->dev, "failed to set PHY clock\n");
245 		return ret;
246 	}
247 
248 	/* enable TX_CLK and PHY_REF_CLK generator */
249 	meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
250 				PRG_ETH0_TX_AND_PHY_REF_CLK);
251 
252 	return 0;
253 }
254 
255 static int meson8b_dwmac_probe(struct platform_device *pdev)
256 {
257 	struct plat_stmmacenet_data *plat_dat;
258 	struct stmmac_resources stmmac_res;
259 	struct resource *res;
260 	struct meson8b_dwmac *dwmac;
261 	int ret;
262 
263 	ret = stmmac_get_platform_resources(pdev, &stmmac_res);
264 	if (ret)
265 		return ret;
266 
267 	plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
268 	if (IS_ERR(plat_dat))
269 		return PTR_ERR(plat_dat);
270 
271 	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
272 	if (!dwmac) {
273 		ret = -ENOMEM;
274 		goto err_remove_config_dt;
275 	}
276 
277 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
278 	dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
279 	if (IS_ERR(dwmac->regs)) {
280 		ret = PTR_ERR(dwmac->regs);
281 		goto err_remove_config_dt;
282 	}
283 
284 	dwmac->pdev = pdev;
285 	dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
286 	if (dwmac->phy_mode < 0) {
287 		dev_err(&pdev->dev, "missing phy-mode property\n");
288 		ret = -EINVAL;
289 		goto err_remove_config_dt;
290 	}
291 
292 	/* use 2ns as fallback since this value was previously hardcoded */
293 	if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
294 				 &dwmac->tx_delay_ns))
295 		dwmac->tx_delay_ns = 2;
296 
297 	ret = meson8b_init_clk(dwmac);
298 	if (ret)
299 		goto err_remove_config_dt;
300 
301 	ret = meson8b_init_prg_eth(dwmac);
302 	if (ret)
303 		goto err_remove_config_dt;
304 
305 	plat_dat->bsp_priv = dwmac;
306 
307 	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
308 	if (ret)
309 		goto err_clk_disable;
310 
311 	return 0;
312 
313 err_clk_disable:
314 	clk_disable_unprepare(dwmac->m25_div_clk);
315 err_remove_config_dt:
316 	stmmac_remove_config_dt(pdev, plat_dat);
317 
318 	return ret;
319 }
320 
321 static int meson8b_dwmac_remove(struct platform_device *pdev)
322 {
323 	struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
324 
325 	clk_disable_unprepare(dwmac->m25_div_clk);
326 
327 	return stmmac_pltfr_remove(pdev);
328 }
329 
330 static const struct of_device_id meson8b_dwmac_match[] = {
331 	{ .compatible = "amlogic,meson8b-dwmac" },
332 	{ .compatible = "amlogic,meson-gxbb-dwmac" },
333 	{ }
334 };
335 MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
336 
337 static struct platform_driver meson8b_dwmac_driver = {
338 	.probe  = meson8b_dwmac_probe,
339 	.remove = meson8b_dwmac_remove,
340 	.driver = {
341 		.name           = "meson8b-dwmac",
342 		.pm		= &stmmac_pltfr_pm_ops,
343 		.of_match_table = meson8b_dwmac_match,
344 	},
345 };
346 module_platform_driver(meson8b_dwmac_driver);
347 
348 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
349 MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
350 MODULE_LICENSE("GPL v2");
351