1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2022 Schneider Electric |
4 | * |
5 | * Clément Léger <clement.leger@bootlin.com> |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/device.h> |
10 | #include <linux/mdio.h> |
11 | #include <linux/of.h> |
12 | #include <linux/of_platform.h> |
13 | #include <linux/pcs-rzn1-miic.h> |
14 | #include <linux/phylink.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <dt-bindings/net/pcs-rzn1-miic.h> |
18 | |
19 | #define MIIC_PRCMD 0x0 |
20 | #define MIIC_ESID_CODE 0x4 |
21 | |
22 | #define MIIC_MODCTRL 0x20 |
23 | #define MIIC_MODCTRL_SW_MODE GENMASK(4, 0) |
24 | |
25 | #define MIIC_CONVCTRL(port) (0x100 + (port) * 4) |
26 | |
27 | #define MIIC_CONVCTRL_CONV_SPEED GENMASK(1, 0) |
28 | #define CONV_MODE_10MBPS 0 |
29 | #define CONV_MODE_100MBPS 1 |
30 | #define CONV_MODE_1000MBPS 2 |
31 | |
32 | #define MIIC_CONVCTRL_CONV_MODE GENMASK(3, 2) |
33 | #define CONV_MODE_MII 0 |
34 | #define CONV_MODE_RMII 1 |
35 | #define CONV_MODE_RGMII 2 |
36 | |
37 | #define MIIC_CONVCTRL_FULLD BIT(8) |
38 | #define MIIC_CONVCTRL_RGMII_LINK BIT(12) |
39 | #define MIIC_CONVCTRL_RGMII_DUPLEX BIT(13) |
40 | #define MIIC_CONVCTRL_RGMII_SPEED GENMASK(15, 14) |
41 | |
42 | #define MIIC_CONVRST 0x114 |
43 | #define MIIC_CONVRST_PHYIF_RST(port) BIT(port) |
44 | #define MIIC_CONVRST_PHYIF_RST_MASK GENMASK(4, 0) |
45 | |
46 | #define MIIC_SWCTRL 0x304 |
47 | #define MIIC_SWDUPC 0x308 |
48 | |
49 | #define MIIC_MAX_NR_PORTS 5 |
50 | |
51 | #define MIIC_MODCTRL_CONF_CONV_NUM 6 |
52 | #define MIIC_MODCTRL_CONF_NONE -1 |
53 | |
54 | /** |
55 | * struct modctrl_match - Matching table entry for convctrl configuration |
56 | * See section 8.2.1 of manual. |
57 | * @mode_cfg: Configuration value for convctrl |
58 | * @conv: Configuration of ethernet port muxes. First index is SWITCH_PORTIN, |
59 | * then index 1 - 5 are CONV1 - CONV5. |
60 | */ |
61 | struct modctrl_match { |
62 | u32 mode_cfg; |
63 | u8 conv[MIIC_MODCTRL_CONF_CONV_NUM]; |
64 | }; |
65 | |
66 | static struct modctrl_match modctrl_match_table[] = { |
67 | {0x0, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
68 | MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, |
69 | {0x1, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
70 | MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
71 | {0x2, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
72 | MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
73 | {0x3, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
74 | MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}}, |
75 | |
76 | {0x8, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
77 | MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, |
78 | {0x9, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
79 | MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
80 | {0xA, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
81 | MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
82 | {0xB, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
83 | MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}}, |
84 | |
85 | {0x10, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
86 | MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, |
87 | {0x11, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
88 | MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
89 | {0x12, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
90 | MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, |
91 | {0x13, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, |
92 | MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}} |
93 | }; |
94 | |
95 | static const char * const conf_to_string[] = { |
96 | [MIIC_GMAC1_PORT] = "GMAC1_PORT" , |
97 | [MIIC_GMAC2_PORT] = "GMAC2_PORT" , |
98 | [MIIC_RTOS_PORT] = "RTOS_PORT" , |
99 | [MIIC_SERCOS_PORTA] = "SERCOS_PORTA" , |
100 | [MIIC_SERCOS_PORTB] = "SERCOS_PORTB" , |
101 | [MIIC_ETHERCAT_PORTA] = "ETHERCAT_PORTA" , |
102 | [MIIC_ETHERCAT_PORTB] = "ETHERCAT_PORTB" , |
103 | [MIIC_ETHERCAT_PORTC] = "ETHERCAT_PORTC" , |
104 | [MIIC_SWITCH_PORTA] = "SWITCH_PORTA" , |
105 | [MIIC_SWITCH_PORTB] = "SWITCH_PORTB" , |
106 | [MIIC_SWITCH_PORTC] = "SWITCH_PORTC" , |
107 | [MIIC_SWITCH_PORTD] = "SWITCH_PORTD" , |
108 | [MIIC_HSR_PORTA] = "HSR_PORTA" , |
109 | [MIIC_HSR_PORTB] = "HSR_PORTB" , |
110 | }; |
111 | |
112 | static const char *index_to_string[MIIC_MODCTRL_CONF_CONV_NUM] = { |
113 | "SWITCH_PORTIN" , |
114 | "CONV1" , |
115 | "CONV2" , |
116 | "CONV3" , |
117 | "CONV4" , |
118 | "CONV5" , |
119 | }; |
120 | |
121 | /** |
122 | * struct miic - MII converter structure |
123 | * @base: base address of the MII converter |
124 | * @dev: Device associated to the MII converter |
125 | * @lock: Lock used for read-modify-write access |
126 | */ |
127 | struct miic { |
128 | void __iomem *base; |
129 | struct device *dev; |
130 | spinlock_t lock; |
131 | }; |
132 | |
133 | /** |
134 | * struct miic_port - Per port MII converter struct |
135 | * @miic: backiling to MII converter structure |
136 | * @pcs: PCS structure associated to the port |
137 | * @port: port number |
138 | * @interface: interface mode of the port |
139 | */ |
140 | struct miic_port { |
141 | struct miic *miic; |
142 | struct phylink_pcs pcs; |
143 | int port; |
144 | phy_interface_t interface; |
145 | }; |
146 | |
147 | static struct miic_port *phylink_pcs_to_miic_port(struct phylink_pcs *pcs) |
148 | { |
149 | return container_of(pcs, struct miic_port, pcs); |
150 | } |
151 | |
152 | static void miic_reg_writel(struct miic *miic, int offset, u32 value) |
153 | { |
154 | writel(val: value, addr: miic->base + offset); |
155 | } |
156 | |
157 | static u32 miic_reg_readl(struct miic *miic, int offset) |
158 | { |
159 | return readl(addr: miic->base + offset); |
160 | } |
161 | |
162 | static void miic_reg_rmw(struct miic *miic, int offset, u32 mask, u32 val) |
163 | { |
164 | u32 reg; |
165 | |
166 | spin_lock(lock: &miic->lock); |
167 | |
168 | reg = miic_reg_readl(miic, offset); |
169 | reg &= ~mask; |
170 | reg |= val; |
171 | miic_reg_writel(miic, offset, value: reg); |
172 | |
173 | spin_unlock(lock: &miic->lock); |
174 | } |
175 | |
176 | static void miic_converter_enable(struct miic *miic, int port, int enable) |
177 | { |
178 | u32 val = 0; |
179 | |
180 | if (enable) |
181 | val = MIIC_CONVRST_PHYIF_RST(port); |
182 | |
183 | miic_reg_rmw(miic, MIIC_CONVRST, MIIC_CONVRST_PHYIF_RST(port), val); |
184 | } |
185 | |
186 | static int miic_config(struct phylink_pcs *pcs, unsigned int neg_mode, |
187 | phy_interface_t interface, |
188 | const unsigned long *advertising, bool permit) |
189 | { |
190 | struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); |
191 | struct miic *miic = miic_port->miic; |
192 | u32 speed, conv_mode, val, mask; |
193 | int port = miic_port->port; |
194 | |
195 | switch (interface) { |
196 | case PHY_INTERFACE_MODE_RMII: |
197 | conv_mode = CONV_MODE_RMII; |
198 | speed = CONV_MODE_100MBPS; |
199 | break; |
200 | case PHY_INTERFACE_MODE_RGMII: |
201 | case PHY_INTERFACE_MODE_RGMII_ID: |
202 | case PHY_INTERFACE_MODE_RGMII_TXID: |
203 | case PHY_INTERFACE_MODE_RGMII_RXID: |
204 | conv_mode = CONV_MODE_RGMII; |
205 | speed = CONV_MODE_1000MBPS; |
206 | break; |
207 | case PHY_INTERFACE_MODE_MII: |
208 | conv_mode = CONV_MODE_MII; |
209 | /* When in MII mode, speed should be set to 0 (which is actually |
210 | * CONV_MODE_10MBPS) |
211 | */ |
212 | speed = CONV_MODE_10MBPS; |
213 | break; |
214 | default: |
215 | return -EOPNOTSUPP; |
216 | } |
217 | |
218 | val = FIELD_PREP(MIIC_CONVCTRL_CONV_MODE, conv_mode); |
219 | mask = MIIC_CONVCTRL_CONV_MODE; |
220 | |
221 | /* Update speed only if we are going to change the interface because |
222 | * the link might already be up and it would break it if the speed is |
223 | * changed. |
224 | */ |
225 | if (interface != miic_port->interface) { |
226 | val |= FIELD_PREP(MIIC_CONVCTRL_CONV_SPEED, speed); |
227 | mask |= MIIC_CONVCTRL_CONV_SPEED; |
228 | miic_port->interface = interface; |
229 | } |
230 | |
231 | miic_reg_rmw(miic, MIIC_CONVCTRL(port), mask, val); |
232 | miic_converter_enable(miic, port: miic_port->port, enable: 1); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | static void miic_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, |
238 | phy_interface_t interface, int speed, int duplex) |
239 | { |
240 | struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); |
241 | struct miic *miic = miic_port->miic; |
242 | u32 conv_speed = 0, val = 0; |
243 | int port = miic_port->port; |
244 | |
245 | if (duplex == DUPLEX_FULL) |
246 | val |= MIIC_CONVCTRL_FULLD; |
247 | |
248 | /* No speed in MII through-mode */ |
249 | if (interface != PHY_INTERFACE_MODE_MII) { |
250 | switch (speed) { |
251 | case SPEED_1000: |
252 | conv_speed = CONV_MODE_1000MBPS; |
253 | break; |
254 | case SPEED_100: |
255 | conv_speed = CONV_MODE_100MBPS; |
256 | break; |
257 | case SPEED_10: |
258 | conv_speed = CONV_MODE_10MBPS; |
259 | break; |
260 | default: |
261 | return; |
262 | } |
263 | } |
264 | |
265 | val |= FIELD_PREP(MIIC_CONVCTRL_CONV_SPEED, conv_speed); |
266 | |
267 | miic_reg_rmw(miic, MIIC_CONVCTRL(port), |
268 | mask: (MIIC_CONVCTRL_CONV_SPEED | MIIC_CONVCTRL_FULLD), val); |
269 | } |
270 | |
271 | static int miic_validate(struct phylink_pcs *pcs, unsigned long *supported, |
272 | const struct phylink_link_state *state) |
273 | { |
274 | if (phy_interface_mode_is_rgmii(mode: state->interface) || |
275 | state->interface == PHY_INTERFACE_MODE_RMII || |
276 | state->interface == PHY_INTERFACE_MODE_MII) |
277 | return 1; |
278 | |
279 | return -EINVAL; |
280 | } |
281 | |
282 | static const struct phylink_pcs_ops miic_phylink_ops = { |
283 | .pcs_validate = miic_validate, |
284 | .pcs_config = miic_config, |
285 | .pcs_link_up = miic_link_up, |
286 | }; |
287 | |
288 | struct phylink_pcs *miic_create(struct device *dev, struct device_node *np) |
289 | { |
290 | struct platform_device *pdev; |
291 | struct miic_port *miic_port; |
292 | struct device_node *pcs_np; |
293 | struct miic *miic; |
294 | u32 port; |
295 | |
296 | if (!of_device_is_available(device: np)) |
297 | return ERR_PTR(error: -ENODEV); |
298 | |
299 | if (of_property_read_u32(np, propname: "reg" , out_value: &port)) |
300 | return ERR_PTR(error: -EINVAL); |
301 | |
302 | if (port > MIIC_MAX_NR_PORTS || port < 1) |
303 | return ERR_PTR(error: -EINVAL); |
304 | |
305 | /* The PCS pdev is attached to the parent node */ |
306 | pcs_np = of_get_parent(node: np); |
307 | if (!pcs_np) |
308 | return ERR_PTR(error: -ENODEV); |
309 | |
310 | if (!of_device_is_available(device: pcs_np)) { |
311 | of_node_put(node: pcs_np); |
312 | return ERR_PTR(error: -ENODEV); |
313 | } |
314 | |
315 | pdev = of_find_device_by_node(np: pcs_np); |
316 | of_node_put(node: pcs_np); |
317 | if (!pdev || !platform_get_drvdata(pdev)) { |
318 | if (pdev) |
319 | put_device(dev: &pdev->dev); |
320 | return ERR_PTR(error: -EPROBE_DEFER); |
321 | } |
322 | |
323 | miic_port = kzalloc(size: sizeof(*miic_port), GFP_KERNEL); |
324 | if (!miic_port) { |
325 | put_device(dev: &pdev->dev); |
326 | return ERR_PTR(error: -ENOMEM); |
327 | } |
328 | |
329 | miic = platform_get_drvdata(pdev); |
330 | device_link_add(consumer: dev, supplier: miic->dev, DL_FLAG_AUTOREMOVE_CONSUMER); |
331 | put_device(dev: &pdev->dev); |
332 | |
333 | miic_port->miic = miic; |
334 | miic_port->port = port - 1; |
335 | miic_port->pcs.ops = &miic_phylink_ops; |
336 | miic_port->pcs.neg_mode = true; |
337 | |
338 | return &miic_port->pcs; |
339 | } |
340 | EXPORT_SYMBOL(miic_create); |
341 | |
342 | void miic_destroy(struct phylink_pcs *pcs) |
343 | { |
344 | struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); |
345 | |
346 | miic_converter_enable(miic: miic_port->miic, port: miic_port->port, enable: 0); |
347 | kfree(objp: miic_port); |
348 | } |
349 | EXPORT_SYMBOL(miic_destroy); |
350 | |
351 | static int miic_init_hw(struct miic *miic, u32 cfg_mode) |
352 | { |
353 | int port; |
354 | |
355 | /* Unlock write access to accessory registers (cf datasheet). If this |
356 | * is going to be used in conjunction with the Cortex-M3, this sequence |
357 | * will have to be moved in register write |
358 | */ |
359 | miic_reg_writel(miic, MIIC_PRCMD, value: 0x00A5); |
360 | miic_reg_writel(miic, MIIC_PRCMD, value: 0x0001); |
361 | miic_reg_writel(miic, MIIC_PRCMD, value: 0xFFFE); |
362 | miic_reg_writel(miic, MIIC_PRCMD, value: 0x0001); |
363 | |
364 | miic_reg_writel(miic, MIIC_MODCTRL, |
365 | FIELD_PREP(MIIC_MODCTRL_SW_MODE, cfg_mode)); |
366 | |
367 | for (port = 0; port < MIIC_MAX_NR_PORTS; port++) { |
368 | miic_converter_enable(miic, port, enable: 0); |
369 | /* Disable speed/duplex control from these registers, datasheet |
370 | * says switch registers should be used to setup switch port |
371 | * speed and duplex. |
372 | */ |
373 | miic_reg_writel(miic, MIIC_SWCTRL, value: 0x0); |
374 | miic_reg_writel(miic, MIIC_SWDUPC, value: 0x0); |
375 | } |
376 | |
377 | return 0; |
378 | } |
379 | |
380 | static bool miic_modctrl_match(s8 table_val[MIIC_MODCTRL_CONF_CONV_NUM], |
381 | s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM]) |
382 | { |
383 | int i; |
384 | |
385 | for (i = 0; i < MIIC_MODCTRL_CONF_CONV_NUM; i++) { |
386 | if (dt_val[i] == MIIC_MODCTRL_CONF_NONE) |
387 | continue; |
388 | |
389 | if (dt_val[i] != table_val[i]) |
390 | return false; |
391 | } |
392 | |
393 | return true; |
394 | } |
395 | |
396 | static void miic_dump_conf(struct device *dev, |
397 | s8 conf[MIIC_MODCTRL_CONF_CONV_NUM]) |
398 | { |
399 | const char *conf_name; |
400 | int i; |
401 | |
402 | for (i = 0; i < MIIC_MODCTRL_CONF_CONV_NUM; i++) { |
403 | if (conf[i] != MIIC_MODCTRL_CONF_NONE) |
404 | conf_name = conf_to_string[conf[i]]; |
405 | else |
406 | conf_name = "NONE" ; |
407 | |
408 | dev_err(dev, "%s: %s\n" , index_to_string[i], conf_name); |
409 | } |
410 | } |
411 | |
412 | static int miic_match_dt_conf(struct device *dev, |
413 | s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM], |
414 | u32 *mode_cfg) |
415 | { |
416 | struct modctrl_match *table_entry; |
417 | int i; |
418 | |
419 | for (i = 0; i < ARRAY_SIZE(modctrl_match_table); i++) { |
420 | table_entry = &modctrl_match_table[i]; |
421 | |
422 | if (miic_modctrl_match(table_val: table_entry->conv, dt_val)) { |
423 | *mode_cfg = table_entry->mode_cfg; |
424 | return 0; |
425 | } |
426 | } |
427 | |
428 | dev_err(dev, "Failed to apply requested configuration\n" ); |
429 | miic_dump_conf(dev, conf: dt_val); |
430 | |
431 | return -EINVAL; |
432 | } |
433 | |
434 | static int miic_parse_dt(struct device *dev, u32 *mode_cfg) |
435 | { |
436 | s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM]; |
437 | struct device_node *np = dev->of_node; |
438 | struct device_node *conv; |
439 | u32 conf; |
440 | int port; |
441 | |
442 | memset(dt_val, MIIC_MODCTRL_CONF_NONE, sizeof(dt_val)); |
443 | |
444 | if (of_property_read_u32(np, propname: "renesas,miic-switch-portin" , out_value: &conf) == 0) |
445 | dt_val[0] = conf; |
446 | |
447 | for_each_child_of_node(np, conv) { |
448 | if (of_property_read_u32(np: conv, propname: "reg" , out_value: &port)) |
449 | continue; |
450 | |
451 | if (!of_device_is_available(device: conv)) |
452 | continue; |
453 | |
454 | if (of_property_read_u32(np: conv, propname: "renesas,miic-input" , out_value: &conf) == 0) |
455 | dt_val[port] = conf; |
456 | } |
457 | |
458 | return miic_match_dt_conf(dev, dt_val, mode_cfg); |
459 | } |
460 | |
461 | static int miic_probe(struct platform_device *pdev) |
462 | { |
463 | struct device *dev = &pdev->dev; |
464 | struct miic *miic; |
465 | u32 mode_cfg; |
466 | int ret; |
467 | |
468 | ret = miic_parse_dt(dev, mode_cfg: &mode_cfg); |
469 | if (ret < 0) |
470 | return ret; |
471 | |
472 | miic = devm_kzalloc(dev, size: sizeof(*miic), GFP_KERNEL); |
473 | if (!miic) |
474 | return -ENOMEM; |
475 | |
476 | spin_lock_init(&miic->lock); |
477 | miic->dev = dev; |
478 | miic->base = devm_platform_ioremap_resource(pdev, index: 0); |
479 | if (IS_ERR(ptr: miic->base)) |
480 | return PTR_ERR(ptr: miic->base); |
481 | |
482 | ret = devm_pm_runtime_enable(dev); |
483 | if (ret < 0) |
484 | return ret; |
485 | |
486 | ret = pm_runtime_resume_and_get(dev); |
487 | if (ret < 0) |
488 | return ret; |
489 | |
490 | ret = miic_init_hw(miic, cfg_mode: mode_cfg); |
491 | if (ret) |
492 | goto disable_runtime_pm; |
493 | |
494 | /* miic_create() relies on that fact that data are attached to the |
495 | * platform device to determine if the driver is ready so this needs to |
496 | * be the last thing to be done after everything is initialized |
497 | * properly. |
498 | */ |
499 | platform_set_drvdata(pdev, data: miic); |
500 | |
501 | return 0; |
502 | |
503 | disable_runtime_pm: |
504 | pm_runtime_put(dev); |
505 | |
506 | return ret; |
507 | } |
508 | |
509 | static void miic_remove(struct platform_device *pdev) |
510 | { |
511 | pm_runtime_put(dev: &pdev->dev); |
512 | } |
513 | |
514 | static const struct of_device_id miic_of_mtable[] = { |
515 | { .compatible = "renesas,rzn1-miic" }, |
516 | { /* sentinel */ }, |
517 | }; |
518 | MODULE_DEVICE_TABLE(of, miic_of_mtable); |
519 | |
520 | static struct platform_driver miic_driver = { |
521 | .driver = { |
522 | .name = "rzn1_miic" , |
523 | .suppress_bind_attrs = true, |
524 | .of_match_table = miic_of_mtable, |
525 | }, |
526 | .probe = miic_probe, |
527 | .remove_new = miic_remove, |
528 | }; |
529 | module_platform_driver(miic_driver); |
530 | |
531 | MODULE_LICENSE("GPL" ); |
532 | MODULE_DESCRIPTION("Renesas MII converter PCS driver" ); |
533 | MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>" ); |
534 | |