aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBiao Huang <biao.huang@mediatek.com>2018-12-12 04:35:31 -0500
committerDavid S. Miller <davem@davemloft.net>2018-12-12 18:21:00 -0500
commit9992f37e346bb00369c261981f2839a5926bf2eb (patch)
treec8a9ed6183a138db6ba3d569b20262b855230552
parent3f9b7eeea0268eba57e2c18b48a9621f38c66101 (diff)
stmmac: dwmac-mediatek: add support for mt2712
Add Ethernet support for MediaTek SoCs from the mt2712 family Signed-off-by: Biao Huang <biao.huang@mediatek.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c408
3 files changed, 417 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 324049eebb9b..6209cc1fb305 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -75,6 +75,14 @@ config DWMAC_LPC18XX
75 ---help--- 75 ---help---
76 Support for NXP LPC18xx/43xx DWMAC Ethernet. 76 Support for NXP LPC18xx/43xx DWMAC Ethernet.
77 77
78config DWMAC_MEDIATEK
79 tristate "MediaTek MT27xx GMAC support"
80 depends on OF && (ARCH_MEDIATEK || COMPILE_TEST)
81 help
82 Support for MediaTek GMAC Ethernet controller.
83
84 This selects the MT2712 SoC support for the stmmac driver.
85
78config DWMAC_MESON 86config DWMAC_MESON
79 tristate "Amlogic Meson dwmac support" 87 tristate "Amlogic Meson dwmac support"
80 default ARCH_MESON 88 default ARCH_MESON
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 99967a80a8c8..bf09701d2623 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
13obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o 13obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
14obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o 14obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
15obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o 15obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
16obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o
16obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o 17obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
17obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o 18obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o
18obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o 19obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
new file mode 100644
index 000000000000..b5db85d298e7
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
@@ -0,0 +1,408 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018 MediaTek Inc.
4 */
5#include <linux/bitfield.h>
6#include <linux/io.h>
7#include <linux/mfd/syscon.h>
8#include <linux/of.h>
9#include <linux/of_device.h>
10#include <linux/of_net.h>
11#include <linux/regmap.h>
12#include <linux/stmmac.h>
13
14#include "stmmac.h"
15#include "stmmac_platform.h"
16
17/* Peri Configuration register for mt2712 */
18#define PERI_ETH_PHY_INTF_SEL 0x418
19#define PHY_INTF_MII 0
20#define PHY_INTF_RGMII 1
21#define PHY_INTF_RMII 4
22#define RMII_CLK_SRC_RXC BIT(4)
23#define RMII_CLK_SRC_INTERNAL BIT(5)
24
25#define PERI_ETH_DLY 0x428
26#define ETH_DLY_GTXC_INV BIT(6)
27#define ETH_DLY_GTXC_ENABLE BIT(5)
28#define ETH_DLY_GTXC_STAGES GENMASK(4, 0)
29#define ETH_DLY_TXC_INV BIT(20)
30#define ETH_DLY_TXC_ENABLE BIT(19)
31#define ETH_DLY_TXC_STAGES GENMASK(18, 14)
32#define ETH_DLY_RXC_INV BIT(13)
33#define ETH_DLY_RXC_ENABLE BIT(12)
34#define ETH_DLY_RXC_STAGES GENMASK(11, 7)
35
36#define PERI_ETH_DLY_FINE 0x800
37#define ETH_RMII_DLY_TX_INV BIT(2)
38#define ETH_FINE_DLY_GTXC BIT(1)
39#define ETH_FINE_DLY_RXC BIT(0)
40
41struct mac_delay_struct {
42 u32 tx_delay;
43 u32 rx_delay;
44 bool tx_inv;
45 bool rx_inv;
46 bool fine_tune;
47};
48
49struct mediatek_dwmac_plat_data {
50 const struct mediatek_dwmac_variant *variant;
51 struct mac_delay_struct mac_delay;
52 struct clk_bulk_data *clks;
53 struct device_node *np;
54 struct regmap *peri_regmap;
55 struct device *dev;
56 int phy_mode;
57 bool rmii_rxc;
58};
59
60struct mediatek_dwmac_variant {
61 int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat);
62 int (*dwmac_set_delay)(struct mediatek_dwmac_plat_data *plat);
63
64 /* clock ids to be requested */
65 const char * const *clk_list;
66 int num_clks;
67
68 u32 dma_bit_mask;
69 u32 rx_delay_max;
70 u32 tx_delay_max;
71};
72
73/* list of clocks required for mac */
74static const char * const mt2712_dwmac_clk_l[] = {
75 "axi", "apb", "mac_main", "ptp_ref"
76};
77
78static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat)
79{
80 int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0;
81 u32 intf_val = 0;
82
83 /* select phy interface in top control domain */
84 switch (plat->phy_mode) {
85 case PHY_INTERFACE_MODE_MII:
86 intf_val |= PHY_INTF_MII;
87 break;
88 case PHY_INTERFACE_MODE_RMII:
89 intf_val |= (PHY_INTF_RMII | rmii_rxc);
90 break;
91 case PHY_INTERFACE_MODE_RGMII:
92 case PHY_INTERFACE_MODE_RGMII_TXID:
93 case PHY_INTERFACE_MODE_RGMII_RXID:
94 case PHY_INTERFACE_MODE_RGMII_ID:
95 intf_val |= PHY_INTF_RGMII;
96 break;
97 default:
98 dev_err(plat->dev, "phy interface not supported\n");
99 return -EINVAL;
100 }
101
102 regmap_write(plat->peri_regmap, PERI_ETH_PHY_INTF_SEL, intf_val);
103
104 return 0;
105}
106
107static void mt2712_delay_ps2stage(struct mac_delay_struct *mac_delay)
108{
109 if (mac_delay->fine_tune) {
110 /* 170ps per stage for fine tune delay macro circuit*/
111 mac_delay->tx_delay /= 170;
112 mac_delay->rx_delay /= 170;
113 } else {
114 /* 550ps per stage for coarse tune delay macro circuit*/
115 mac_delay->tx_delay /= 550;
116 mac_delay->rx_delay /= 550;
117 }
118}
119
120static int mt2712_set_delay(struct mediatek_dwmac_plat_data *plat)
121{
122 struct mac_delay_struct *mac_delay = &plat->mac_delay;
123 u32 delay_val = 0, fine_val = 0;
124
125 mt2712_delay_ps2stage(mac_delay);
126
127 switch (plat->phy_mode) {
128 case PHY_INTERFACE_MODE_MII:
129 delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->tx_delay);
130 delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->tx_delay);
131 delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->tx_inv);
132
133 delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
134 delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
135 delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
136 break;
137 case PHY_INTERFACE_MODE_RMII:
138 /* the rmii reference clock is from external phy,
139 * and the property "rmii_rxc" indicates which pin(TXC/RXC)
140 * the reference clk is connected to. The reference clock is a
141 * received signal, so rx_delay/rx_inv are used to indicate
142 * the reference clock timing adjustment
143 */
144 if (plat->rmii_rxc) {
145 /* the rmii reference clock from outside is connected
146 * to RXC pin, the reference clock will be adjusted
147 * by RXC delay macro circuit.
148 */
149 delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
150 delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
151 delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
152 } else {
153 /* the rmii reference clock from outside is connected
154 * to TXC pin, the reference clock will be adjusted
155 * by TXC delay macro circuit.
156 */
157 delay_val |= FIELD_PREP(ETH_DLY_TXC_ENABLE, !!mac_delay->rx_delay);
158 delay_val |= FIELD_PREP(ETH_DLY_TXC_STAGES, mac_delay->rx_delay);
159 delay_val |= FIELD_PREP(ETH_DLY_TXC_INV, mac_delay->rx_inv);
160 }
161 /* tx_inv will inverse the tx clock inside mac relateive to
162 * reference clock from external phy,
163 * and this bit is located in the same register with fine-tune
164 */
165 if (mac_delay->tx_inv)
166 fine_val = ETH_RMII_DLY_TX_INV;
167 break;
168 case PHY_INTERFACE_MODE_RGMII:
169 /* the PHY is not responsible for inserting any internal
170 * delay by itself in PHY_INTERFACE_MODE_RGMII case,
171 * so Ethernet MAC will insert delays for both transmit
172 * and receive path here.
173 */
174 if (mac_delay->fine_tune)
175 fine_val = ETH_FINE_DLY_GTXC | ETH_FINE_DLY_RXC;
176
177 delay_val |= FIELD_PREP(ETH_DLY_GTXC_ENABLE, !!mac_delay->tx_delay);
178 delay_val |= FIELD_PREP(ETH_DLY_GTXC_STAGES, mac_delay->tx_delay);
179 delay_val |= FIELD_PREP(ETH_DLY_GTXC_INV, mac_delay->tx_inv);
180
181 delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
182 delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
183 delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
184 break;
185 case PHY_INTERFACE_MODE_RGMII_TXID:
186 /* the PHY should insert an internal delay for the transmit
187 * path in PHY_INTERFACE_MODE_RGMII_TXID case,
188 * so Ethernet MAC will insert the delay for receive path here.
189 */
190 if (mac_delay->fine_tune)
191 fine_val = ETH_FINE_DLY_RXC;
192
193 delay_val |= FIELD_PREP(ETH_DLY_RXC_ENABLE, !!mac_delay->rx_delay);
194 delay_val |= FIELD_PREP(ETH_DLY_RXC_STAGES, mac_delay->rx_delay);
195 delay_val |= FIELD_PREP(ETH_DLY_RXC_INV, mac_delay->rx_inv);
196 break;
197 case PHY_INTERFACE_MODE_RGMII_RXID:
198 /* the PHY should insert an internal delay for the receive
199 * path in PHY_INTERFACE_MODE_RGMII_RXID case,
200 * so Ethernet MAC will insert the delay for transmit path here.
201 */
202 if (mac_delay->fine_tune)
203 fine_val = ETH_FINE_DLY_GTXC;
204
205 delay_val |= FIELD_PREP(ETH_DLY_GTXC_ENABLE, !!mac_delay->tx_delay);
206 delay_val |= FIELD_PREP(ETH_DLY_GTXC_STAGES, mac_delay->tx_delay);
207 delay_val |= FIELD_PREP(ETH_DLY_GTXC_INV, mac_delay->tx_inv);
208 break;
209 case PHY_INTERFACE_MODE_RGMII_ID:
210 /* the PHY should insert internal delays for both transmit
211 * and receive path in PHY_INTERFACE_MODE_RGMII_RXID case,
212 * so Ethernet MAC will NOT insert any delay here.
213 */
214 break;
215 default:
216 dev_err(plat->dev, "phy interface not supported\n");
217 return -EINVAL;
218 }
219 regmap_write(plat->peri_regmap, PERI_ETH_DLY, delay_val);
220 regmap_write(plat->peri_regmap, PERI_ETH_DLY_FINE, fine_val);
221
222 return 0;
223}
224
225static const struct mediatek_dwmac_variant mt2712_gmac_variant = {
226 .dwmac_set_phy_interface = mt2712_set_interface,
227 .dwmac_set_delay = mt2712_set_delay,
228 .clk_list = mt2712_dwmac_clk_l,
229 .num_clks = ARRAY_SIZE(mt2712_dwmac_clk_l),
230 .dma_bit_mask = 33,
231 .rx_delay_max = 17600,
232 .tx_delay_max = 17600,
233};
234
235static int mediatek_dwmac_config_dt(struct mediatek_dwmac_plat_data *plat)
236{
237 struct mac_delay_struct *mac_delay = &plat->mac_delay;
238 u32 tx_delay_ps, rx_delay_ps;
239
240 plat->peri_regmap = syscon_regmap_lookup_by_phandle(plat->np, "mediatek,pericfg");
241 if (IS_ERR(plat->peri_regmap)) {
242 dev_err(plat->dev, "Failed to get pericfg syscon\n");
243 return PTR_ERR(plat->peri_regmap);
244 }
245
246 plat->phy_mode = of_get_phy_mode(plat->np);
247 if (plat->phy_mode < 0) {
248 dev_err(plat->dev, "not find phy-mode\n");
249 return -EINVAL;
250 }
251
252 if (!of_property_read_u32(plat->np, "mediatek,tx-delay-ps", &tx_delay_ps)) {
253 if (tx_delay_ps < plat->variant->tx_delay_max) {
254 mac_delay->tx_delay = tx_delay_ps;
255 } else {
256 dev_err(plat->dev, "Invalid TX clock delay: %dps\n", tx_delay_ps);
257 return -EINVAL;
258 }
259 }
260
261 if (!of_property_read_u32(plat->np, "mediatek,rx-delay-ps", &rx_delay_ps)) {
262 if (rx_delay_ps < plat->variant->rx_delay_max) {
263 mac_delay->rx_delay = rx_delay_ps;
264 } else {
265 dev_err(plat->dev, "Invalid RX clock delay: %dps\n", rx_delay_ps);
266 return -EINVAL;
267 }
268 }
269
270 mac_delay->tx_inv = of_property_read_bool(plat->np, "mediatek,txc-inverse");
271 mac_delay->rx_inv = of_property_read_bool(plat->np, "mediatek,rxc-inverse");
272 mac_delay->fine_tune = of_property_read_bool(plat->np, "mediatek,fine-tune");
273 plat->rmii_rxc = of_property_read_bool(plat->np, "mediatek,rmii-rxc");
274
275 return 0;
276}
277
278static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat)
279{
280 const struct mediatek_dwmac_variant *variant = plat->variant;
281 int i, num = variant->num_clks;
282
283 plat->clks = devm_kcalloc(plat->dev, num, sizeof(*plat->clks), GFP_KERNEL);
284 if (!plat->clks)
285 return -ENOMEM;
286
287 for (i = 0; i < num; i++)
288 plat->clks[i].id = variant->clk_list[i];
289
290 return devm_clk_bulk_get(plat->dev, num, plat->clks);
291}
292
293static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
294{
295 struct mediatek_dwmac_plat_data *plat = priv;
296 const struct mediatek_dwmac_variant *variant = plat->variant;
297 int ret;
298
299 ret = dma_set_mask_and_coherent(plat->dev, DMA_BIT_MASK(variant->dma_bit_mask));
300 if (ret) {
301 dev_err(plat->dev, "No suitable DMA available, err = %d\n", ret);
302 return ret;
303 }
304
305 ret = variant->dwmac_set_phy_interface(plat);
306 if (ret) {
307 dev_err(plat->dev, "failed to set phy interface, err = %d\n", ret);
308 return ret;
309 }
310
311 ret = variant->dwmac_set_delay(plat);
312 if (ret) {
313 dev_err(plat->dev, "failed to set delay value, err = %d\n", ret);
314 return ret;
315 }
316
317 ret = clk_bulk_prepare_enable(variant->num_clks, plat->clks);
318 if (ret) {
319 dev_err(plat->dev, "failed to enable clks, err = %d\n", ret);
320 return ret;
321 }
322
323 return 0;
324}
325
326static void mediatek_dwmac_exit(struct platform_device *pdev, void *priv)
327{
328 struct mediatek_dwmac_plat_data *plat = priv;
329 const struct mediatek_dwmac_variant *variant = plat->variant;
330
331 clk_bulk_disable_unprepare(variant->num_clks, plat->clks);
332}
333
334static int mediatek_dwmac_probe(struct platform_device *pdev)
335{
336 struct mediatek_dwmac_plat_data *priv_plat;
337 struct plat_stmmacenet_data *plat_dat;
338 struct stmmac_resources stmmac_res;
339 int ret;
340
341 priv_plat = devm_kzalloc(&pdev->dev, sizeof(*priv_plat), GFP_KERNEL);
342 if (!priv_plat)
343 return -ENOMEM;
344
345 priv_plat->variant = of_device_get_match_data(&pdev->dev);
346 if (!priv_plat->variant) {
347 dev_err(&pdev->dev, "Missing dwmac-mediatek variant\n");
348 return -EINVAL;
349 }
350
351 priv_plat->dev = &pdev->dev;
352 priv_plat->np = pdev->dev.of_node;
353
354 ret = mediatek_dwmac_config_dt(priv_plat);
355 if (ret)
356 return ret;
357
358 ret = mediatek_dwmac_clk_init(priv_plat);
359 if (ret)
360 return ret;
361
362 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
363 if (ret)
364 return ret;
365
366 plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
367 if (IS_ERR(plat_dat))
368 return PTR_ERR(plat_dat);
369
370 plat_dat->interface = priv_plat->phy_mode;
371 /* clk_csr_i = 250-300MHz & MDC = clk_csr_i/124 */
372 plat_dat->clk_csr = 5;
373 plat_dat->has_gmac4 = 1;
374 plat_dat->has_gmac = 0;
375 plat_dat->pmt = 0;
376 plat_dat->maxmtu = ETH_DATA_LEN;
377 plat_dat->bsp_priv = priv_plat;
378 plat_dat->init = mediatek_dwmac_init;
379 plat_dat->exit = mediatek_dwmac_exit;
380 mediatek_dwmac_init(pdev, priv_plat);
381
382 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
383 if (ret) {
384 stmmac_remove_config_dt(pdev, plat_dat);
385 return ret;
386 }
387
388 return 0;
389}
390
391static const struct of_device_id mediatek_dwmac_match[] = {
392 { .compatible = "mediatek,mt2712-gmac",
393 .data = &mt2712_gmac_variant },
394 { }
395};
396
397MODULE_DEVICE_TABLE(of, mediatek_dwmac_match);
398
399static struct platform_driver mediatek_dwmac_driver = {
400 .probe = mediatek_dwmac_probe,
401 .remove = stmmac_pltfr_remove,
402 .driver = {
403 .name = "dwmac-mediatek",
404 .pm = &stmmac_pltfr_pm_ops,
405 .of_match_table = mediatek_dwmac_match,
406 },
407};
408module_platform_driver(mediatek_dwmac_driver);