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