1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer |
4 | * |
5 | * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
6 | */ |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/clk.h> |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/device.h> |
12 | #include <linux/ethtool.h> |
13 | #include <linux/io.h> |
14 | #include <linux/ioport.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_net.h> |
18 | #include <linux/mfd/syscon.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/stmmac.h> |
21 | |
22 | #include "stmmac_platform.h" |
23 | |
24 | #define PRG_ETH0 0x0 |
25 | |
26 | #define PRG_ETH0_RGMII_MODE BIT(0) |
27 | |
28 | #define PRG_ETH0_EXT_PHY_MODE_MASK GENMASK(2, 0) |
29 | #define PRG_ETH0_EXT_RGMII_MODE 1 |
30 | #define PRG_ETH0_EXT_RMII_MODE 4 |
31 | |
32 | /* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */ |
33 | #define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4) |
34 | |
35 | /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where 8ns are exactly one |
36 | * cycle of the 125MHz RGMII TX clock): |
37 | * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3 |
38 | */ |
39 | #define PRG_ETH0_TXDLY_MASK GENMASK(6, 5) |
40 | |
41 | /* divider for the result of m250_sel */ |
42 | #define PRG_ETH0_CLK_M250_DIV_SHIFT 7 |
43 | #define PRG_ETH0_CLK_M250_DIV_WIDTH 3 |
44 | |
45 | #define PRG_ETH0_RGMII_TX_CLK_EN 10 |
46 | |
47 | #define PRG_ETH0_INVERTED_RMII_CLK BIT(11) |
48 | #define PRG_ETH0_TX_AND_PHY_REF_CLK BIT(12) |
49 | |
50 | /* Bypass (= 0, the signal from the GPIO input directly connects to the |
51 | * internal sampling) or enable (= 1) the internal logic for RXEN and RXD[3:0] |
52 | * timing tuning. |
53 | */ |
54 | #define PRG_ETH0_ADJ_ENABLE BIT(13) |
55 | /* Controls whether the RXEN and RXD[3:0] signals should be aligned with the |
56 | * input RX rising/falling edge and sent to the Ethernet internals. This sets |
57 | * the automatically delay and skew automatically (internally). |
58 | */ |
59 | #define PRG_ETH0_ADJ_SETUP BIT(14) |
60 | /* An internal counter based on the "timing-adjustment" clock. The counter is |
61 | * cleared on both, the falling and rising edge of the RX_CLK. This selects the |
62 | * delay (= the counter value) when to start sampling RXEN and RXD[3:0]. |
63 | */ |
64 | #define PRG_ETH0_ADJ_DELAY GENMASK(19, 15) |
65 | /* Adjusts the skew between each bit of RXEN and RXD[3:0]. If a signal has a |
66 | * large input delay, the bit for that signal (RXEN = bit 0, RXD[3] = bit 1, |
67 | * ...) can be configured to be 1 to compensate for a delay of about 1ns. |
68 | */ |
69 | #define PRG_ETH0_ADJ_SKEW GENMASK(24, 20) |
70 | |
71 | #define PRG_ETH1 0x4 |
72 | |
73 | /* Defined for adding a delay to the input RX_CLK for better timing. |
74 | * Each step is 200ps. These bits are used with external RGMII PHYs |
75 | * because RGMII RX only has the small window. cfg_rxclk_dly can |
76 | * adjust the window between RX_CLK and RX_DATA and improve the stability |
77 | * of "rx data valid". |
78 | */ |
79 | #define PRG_ETH1_CFG_RXCLK_DLY GENMASK(19, 16) |
80 | |
81 | struct meson8b_dwmac; |
82 | |
83 | struct meson8b_dwmac_data { |
84 | int (*set_phy_mode)(struct meson8b_dwmac *dwmac); |
85 | bool has_prg_eth1_rgmii_rx_delay; |
86 | }; |
87 | |
88 | struct meson8b_dwmac { |
89 | struct device *dev; |
90 | void __iomem *regs; |
91 | |
92 | const struct meson8b_dwmac_data *data; |
93 | phy_interface_t phy_mode; |
94 | struct clk *rgmii_tx_clk; |
95 | u32 tx_delay_ns; |
96 | u32 rx_delay_ps; |
97 | struct clk *timing_adj_clk; |
98 | }; |
99 | |
100 | struct meson8b_dwmac_clk_configs { |
101 | struct clk_mux m250_mux; |
102 | struct clk_divider m250_div; |
103 | struct clk_fixed_factor fixed_div2; |
104 | struct clk_gate rgmii_tx_en; |
105 | }; |
106 | |
107 | static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg, |
108 | u32 mask, u32 value) |
109 | { |
110 | u32 data; |
111 | |
112 | data = readl(addr: dwmac->regs + reg); |
113 | data &= ~mask; |
114 | data |= (value & mask); |
115 | |
116 | writel(val: data, addr: dwmac->regs + reg); |
117 | } |
118 | |
119 | static struct clk *meson8b_dwmac_register_clk(struct meson8b_dwmac *dwmac, |
120 | const char *name_suffix, |
121 | const struct clk_parent_data *parents, |
122 | int num_parents, |
123 | const struct clk_ops *ops, |
124 | struct clk_hw *hw) |
125 | { |
126 | struct clk_init_data init = { }; |
127 | char clk_name[32]; |
128 | |
129 | snprintf(buf: clk_name, size: sizeof(clk_name), fmt: "%s#%s" , dev_name(dev: dwmac->dev), |
130 | name_suffix); |
131 | |
132 | init.name = clk_name; |
133 | init.ops = ops; |
134 | init.flags = CLK_SET_RATE_PARENT; |
135 | init.parent_data = parents; |
136 | init.num_parents = num_parents; |
137 | |
138 | hw->init = &init; |
139 | |
140 | return devm_clk_register(dev: dwmac->dev, hw); |
141 | } |
142 | |
143 | static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac) |
144 | { |
145 | struct clk *clk; |
146 | struct device *dev = dwmac->dev; |
147 | static const struct clk_parent_data mux_parents[] = { |
148 | { .fw_name = "clkin0" , }, |
149 | { .index = -1, }, |
150 | }; |
151 | static const struct clk_div_table div_table[] = { |
152 | { .div = 2, .val = 2, }, |
153 | { .div = 3, .val = 3, }, |
154 | { .div = 4, .val = 4, }, |
155 | { .div = 5, .val = 5, }, |
156 | { .div = 6, .val = 6, }, |
157 | { .div = 7, .val = 7, }, |
158 | { /* end of array */ } |
159 | }; |
160 | struct meson8b_dwmac_clk_configs *clk_configs; |
161 | struct clk_parent_data parent_data = { }; |
162 | |
163 | clk_configs = devm_kzalloc(dev, size: sizeof(*clk_configs), GFP_KERNEL); |
164 | if (!clk_configs) |
165 | return -ENOMEM; |
166 | |
167 | clk_configs->m250_mux.reg = dwmac->regs + PRG_ETH0; |
168 | clk_configs->m250_mux.shift = __ffs(PRG_ETH0_CLK_M250_SEL_MASK); |
169 | clk_configs->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK >> |
170 | clk_configs->m250_mux.shift; |
171 | clk = meson8b_dwmac_register_clk(dwmac, name_suffix: "m250_sel" , parents: mux_parents, |
172 | ARRAY_SIZE(mux_parents), ops: &clk_mux_ops, |
173 | hw: &clk_configs->m250_mux.hw); |
174 | if (WARN_ON(IS_ERR(clk))) |
175 | return PTR_ERR(ptr: clk); |
176 | |
177 | parent_data.hw = &clk_configs->m250_mux.hw; |
178 | clk_configs->m250_div.reg = dwmac->regs + PRG_ETH0; |
179 | clk_configs->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT; |
180 | clk_configs->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH; |
181 | clk_configs->m250_div.table = div_table; |
182 | clk_configs->m250_div.flags = CLK_DIVIDER_ALLOW_ZERO | |
183 | CLK_DIVIDER_ROUND_CLOSEST; |
184 | clk = meson8b_dwmac_register_clk(dwmac, name_suffix: "m250_div" , parents: &parent_data, num_parents: 1, |
185 | ops: &clk_divider_ops, |
186 | hw: &clk_configs->m250_div.hw); |
187 | if (WARN_ON(IS_ERR(clk))) |
188 | return PTR_ERR(ptr: clk); |
189 | |
190 | parent_data.hw = &clk_configs->m250_div.hw; |
191 | clk_configs->fixed_div2.mult = 1; |
192 | clk_configs->fixed_div2.div = 2; |
193 | clk = meson8b_dwmac_register_clk(dwmac, name_suffix: "fixed_div2" , parents: &parent_data, num_parents: 1, |
194 | ops: &clk_fixed_factor_ops, |
195 | hw: &clk_configs->fixed_div2.hw); |
196 | if (WARN_ON(IS_ERR(clk))) |
197 | return PTR_ERR(ptr: clk); |
198 | |
199 | parent_data.hw = &clk_configs->fixed_div2.hw; |
200 | clk_configs->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0; |
201 | clk_configs->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN; |
202 | clk = meson8b_dwmac_register_clk(dwmac, name_suffix: "rgmii_tx_en" , parents: &parent_data, num_parents: 1, |
203 | ops: &clk_gate_ops, |
204 | hw: &clk_configs->rgmii_tx_en.hw); |
205 | if (WARN_ON(IS_ERR(clk))) |
206 | return PTR_ERR(ptr: clk); |
207 | |
208 | dwmac->rgmii_tx_clk = clk; |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static int meson8b_set_phy_mode(struct meson8b_dwmac *dwmac) |
214 | { |
215 | switch (dwmac->phy_mode) { |
216 | case PHY_INTERFACE_MODE_RGMII: |
217 | case PHY_INTERFACE_MODE_RGMII_RXID: |
218 | case PHY_INTERFACE_MODE_RGMII_ID: |
219 | case PHY_INTERFACE_MODE_RGMII_TXID: |
220 | /* enable RGMII mode */ |
221 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
222 | PRG_ETH0_RGMII_MODE, |
223 | PRG_ETH0_RGMII_MODE); |
224 | break; |
225 | case PHY_INTERFACE_MODE_RMII: |
226 | /* disable RGMII mode -> enables RMII mode */ |
227 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
228 | PRG_ETH0_RGMII_MODE, value: 0); |
229 | break; |
230 | default: |
231 | dev_err(dwmac->dev, "fail to set phy-mode %s\n" , |
232 | phy_modes(dwmac->phy_mode)); |
233 | return -EINVAL; |
234 | } |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac) |
240 | { |
241 | switch (dwmac->phy_mode) { |
242 | case PHY_INTERFACE_MODE_RGMII: |
243 | case PHY_INTERFACE_MODE_RGMII_RXID: |
244 | case PHY_INTERFACE_MODE_RGMII_ID: |
245 | case PHY_INTERFACE_MODE_RGMII_TXID: |
246 | /* enable RGMII mode */ |
247 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
248 | PRG_ETH0_EXT_PHY_MODE_MASK, |
249 | PRG_ETH0_EXT_RGMII_MODE); |
250 | break; |
251 | case PHY_INTERFACE_MODE_RMII: |
252 | /* disable RGMII mode -> enables RMII mode */ |
253 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
254 | PRG_ETH0_EXT_PHY_MODE_MASK, |
255 | PRG_ETH0_EXT_RMII_MODE); |
256 | break; |
257 | default: |
258 | dev_err(dwmac->dev, "fail to set phy-mode %s\n" , |
259 | phy_modes(dwmac->phy_mode)); |
260 | return -EINVAL; |
261 | } |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | static void meson8b_clk_disable_unprepare(void *data) |
267 | { |
268 | clk_disable_unprepare(clk: data); |
269 | } |
270 | |
271 | static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac, |
272 | struct clk *clk) |
273 | { |
274 | int ret; |
275 | |
276 | ret = clk_prepare_enable(clk); |
277 | if (ret) |
278 | return ret; |
279 | |
280 | return devm_add_action_or_reset(dwmac->dev, |
281 | meson8b_clk_disable_unprepare, clk); |
282 | } |
283 | |
284 | static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac) |
285 | { |
286 | u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config; |
287 | int ret; |
288 | |
289 | rx_adj_config = 0; |
290 | cfg_rxclk_dly = 0; |
291 | tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK, |
292 | dwmac->tx_delay_ns >> 1); |
293 | |
294 | if (dwmac->data->has_prg_eth1_rgmii_rx_delay) |
295 | cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY, |
296 | dwmac->rx_delay_ps / 200); |
297 | else if (dwmac->rx_delay_ps == 2000) |
298 | rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP; |
299 | |
300 | switch (dwmac->phy_mode) { |
301 | case PHY_INTERFACE_MODE_RGMII: |
302 | delay_config = tx_dly_config | rx_adj_config; |
303 | break; |
304 | case PHY_INTERFACE_MODE_RGMII_RXID: |
305 | delay_config = tx_dly_config; |
306 | cfg_rxclk_dly = 0; |
307 | break; |
308 | case PHY_INTERFACE_MODE_RGMII_TXID: |
309 | delay_config = rx_adj_config; |
310 | break; |
311 | case PHY_INTERFACE_MODE_RGMII_ID: |
312 | case PHY_INTERFACE_MODE_RMII: |
313 | delay_config = 0; |
314 | cfg_rxclk_dly = 0; |
315 | break; |
316 | default: |
317 | dev_err(dwmac->dev, "unsupported phy-mode %s\n" , |
318 | phy_modes(dwmac->phy_mode)); |
319 | return -EINVAL; |
320 | } |
321 | |
322 | if (delay_config & PRG_ETH0_ADJ_ENABLE) { |
323 | if (!dwmac->timing_adj_clk) { |
324 | dev_err(dwmac->dev, |
325 | "The timing-adjustment clock is mandatory for the RX delay re-timing\n" ); |
326 | return -EINVAL; |
327 | } |
328 | |
329 | /* The timing adjustment logic is driven by a separate clock */ |
330 | ret = meson8b_devm_clk_prepare_enable(dwmac, |
331 | clk: dwmac->timing_adj_clk); |
332 | if (ret) { |
333 | dev_err(dwmac->dev, |
334 | "Failed to enable the timing-adjustment clock\n" ); |
335 | return ret; |
336 | } |
337 | } |
338 | |
339 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK | |
340 | PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP | |
341 | PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW, |
342 | value: delay_config); |
343 | |
344 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY, |
345 | value: cfg_rxclk_dly); |
346 | |
347 | return 0; |
348 | } |
349 | |
350 | static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac) |
351 | { |
352 | int ret; |
353 | |
354 | if (phy_interface_mode_is_rgmii(mode: dwmac->phy_mode)) { |
355 | /* only relevant for RMII mode -> disable in RGMII mode */ |
356 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
357 | PRG_ETH0_INVERTED_RMII_CLK, value: 0); |
358 | |
359 | /* Configure the 125MHz RGMII TX clock, the IP block changes |
360 | * the output automatically (= without us having to configure |
361 | * a register) based on the line-speed (125MHz for Gbit speeds, |
362 | * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s). |
363 | */ |
364 | ret = clk_set_rate(clk: dwmac->rgmii_tx_clk, rate: 125 * 1000 * 1000); |
365 | if (ret) { |
366 | dev_err(dwmac->dev, |
367 | "failed to set RGMII TX clock\n" ); |
368 | return ret; |
369 | } |
370 | |
371 | ret = meson8b_devm_clk_prepare_enable(dwmac, |
372 | clk: dwmac->rgmii_tx_clk); |
373 | if (ret) { |
374 | dev_err(dwmac->dev, |
375 | "failed to enable the RGMII TX clock\n" ); |
376 | return ret; |
377 | } |
378 | } else { |
379 | /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */ |
380 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, |
381 | PRG_ETH0_INVERTED_RMII_CLK, |
382 | PRG_ETH0_INVERTED_RMII_CLK); |
383 | } |
384 | |
385 | /* enable TX_CLK and PHY_REF_CLK generator */ |
386 | meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK, |
387 | PRG_ETH0_TX_AND_PHY_REF_CLK); |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | static int meson8b_dwmac_probe(struct platform_device *pdev) |
393 | { |
394 | struct plat_stmmacenet_data *plat_dat; |
395 | struct stmmac_resources stmmac_res; |
396 | struct meson8b_dwmac *dwmac; |
397 | int ret; |
398 | |
399 | ret = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
400 | if (ret) |
401 | return ret; |
402 | |
403 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
404 | if (IS_ERR(ptr: plat_dat)) |
405 | return PTR_ERR(ptr: plat_dat); |
406 | |
407 | dwmac = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dwmac), GFP_KERNEL); |
408 | if (!dwmac) |
409 | return -ENOMEM; |
410 | |
411 | dwmac->data = (const struct meson8b_dwmac_data *) |
412 | of_device_get_match_data(dev: &pdev->dev); |
413 | if (!dwmac->data) |
414 | return -EINVAL; |
415 | dwmac->regs = devm_platform_ioremap_resource(pdev, index: 1); |
416 | if (IS_ERR(ptr: dwmac->regs)) |
417 | return PTR_ERR(ptr: dwmac->regs); |
418 | |
419 | dwmac->dev = &pdev->dev; |
420 | ret = of_get_phy_mode(np: pdev->dev.of_node, interface: &dwmac->phy_mode); |
421 | if (ret) { |
422 | dev_err(&pdev->dev, "missing phy-mode property\n" ); |
423 | return ret; |
424 | } |
425 | |
426 | /* use 2ns as fallback since this value was previously hardcoded */ |
427 | if (of_property_read_u32(np: pdev->dev.of_node, propname: "amlogic,tx-delay-ns" , |
428 | out_value: &dwmac->tx_delay_ns)) |
429 | dwmac->tx_delay_ns = 2; |
430 | |
431 | /* RX delay defaults to 0ps since this is what many boards use */ |
432 | if (of_property_read_u32(np: pdev->dev.of_node, propname: "rx-internal-delay-ps" , |
433 | out_value: &dwmac->rx_delay_ps)) { |
434 | if (!of_property_read_u32(np: pdev->dev.of_node, |
435 | propname: "amlogic,rx-delay-ns" , |
436 | out_value: &dwmac->rx_delay_ps)) |
437 | /* convert ns to ps */ |
438 | dwmac->rx_delay_ps *= 1000; |
439 | } |
440 | |
441 | if (dwmac->data->has_prg_eth1_rgmii_rx_delay) { |
442 | if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) { |
443 | dev_err(dwmac->dev, |
444 | "The RGMII RX delay range is 0..3000ps in 200ps steps" ); |
445 | return -EINVAL; |
446 | } |
447 | } else { |
448 | if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) { |
449 | dev_err(dwmac->dev, |
450 | "The only allowed RGMII RX delays values are: 0ps, 2000ps" ); |
451 | return -EINVAL; |
452 | } |
453 | } |
454 | |
455 | dwmac->timing_adj_clk = devm_clk_get_optional(dev: dwmac->dev, |
456 | id: "timing-adjustment" ); |
457 | if (IS_ERR(ptr: dwmac->timing_adj_clk)) |
458 | return PTR_ERR(ptr: dwmac->timing_adj_clk); |
459 | |
460 | ret = meson8b_init_rgmii_delays(dwmac); |
461 | if (ret) |
462 | return ret; |
463 | |
464 | ret = meson8b_init_rgmii_tx_clk(dwmac); |
465 | if (ret) |
466 | return ret; |
467 | |
468 | ret = dwmac->data->set_phy_mode(dwmac); |
469 | if (ret) |
470 | return ret; |
471 | |
472 | ret = meson8b_init_prg_eth(dwmac); |
473 | if (ret) |
474 | return ret; |
475 | |
476 | plat_dat->bsp_priv = dwmac; |
477 | |
478 | return stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
479 | } |
480 | |
481 | static const struct meson8b_dwmac_data meson8b_dwmac_data = { |
482 | .set_phy_mode = meson8b_set_phy_mode, |
483 | .has_prg_eth1_rgmii_rx_delay = false, |
484 | }; |
485 | |
486 | static const struct meson8b_dwmac_data meson_axg_dwmac_data = { |
487 | .set_phy_mode = meson_axg_set_phy_mode, |
488 | .has_prg_eth1_rgmii_rx_delay = false, |
489 | }; |
490 | |
491 | static const struct meson8b_dwmac_data meson_g12a_dwmac_data = { |
492 | .set_phy_mode = meson_axg_set_phy_mode, |
493 | .has_prg_eth1_rgmii_rx_delay = true, |
494 | }; |
495 | |
496 | static const struct of_device_id meson8b_dwmac_match[] = { |
497 | { |
498 | .compatible = "amlogic,meson8b-dwmac" , |
499 | .data = &meson8b_dwmac_data, |
500 | }, |
501 | { |
502 | .compatible = "amlogic,meson8m2-dwmac" , |
503 | .data = &meson8b_dwmac_data, |
504 | }, |
505 | { |
506 | .compatible = "amlogic,meson-gxbb-dwmac" , |
507 | .data = &meson8b_dwmac_data, |
508 | }, |
509 | { |
510 | .compatible = "amlogic,meson-axg-dwmac" , |
511 | .data = &meson_axg_dwmac_data, |
512 | }, |
513 | { |
514 | .compatible = "amlogic,meson-g12a-dwmac" , |
515 | .data = &meson_g12a_dwmac_data, |
516 | }, |
517 | { } |
518 | }; |
519 | MODULE_DEVICE_TABLE(of, meson8b_dwmac_match); |
520 | |
521 | static struct platform_driver meson8b_dwmac_driver = { |
522 | .probe = meson8b_dwmac_probe, |
523 | .remove_new = stmmac_pltfr_remove, |
524 | .driver = { |
525 | .name = "meson8b-dwmac" , |
526 | .pm = &stmmac_pltfr_pm_ops, |
527 | .of_match_table = meson8b_dwmac_match, |
528 | }, |
529 | }; |
530 | module_platform_driver(meson8b_dwmac_driver); |
531 | |
532 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>" ); |
533 | MODULE_DESCRIPTION("Amlogic Meson8b, Meson8m2 and GXBB DWMAC glue layer" ); |
534 | MODULE_LICENSE("GPL v2" ); |
535 | |