Commit | Line | Data |
---|---|---|
1577ecef AF |
1 | /* |
2 | * Freescale PowerQUICC Ethernet Driver -- MIIM bus implementation | |
3 | * Provides Bus interface for MIIM regs | |
4 | * | |
5 | * Author: Andy Fleming <afleming@freescale.com> | |
1d2397d7 | 6 | * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com> |
1577ecef | 7 | * |
1d2397d7 | 8 | * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc. |
1577ecef AF |
9 | * |
10 | * Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips) | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/errno.h> | |
1577ecef | 22 | #include <linux/slab.h> |
1577ecef | 23 | #include <linux/delay.h> |
1577ecef | 24 | #include <linux/module.h> |
1577ecef | 25 | #include <linux/mii.h> |
22ae782f | 26 | #include <linux/of_address.h> |
324931ba | 27 | #include <linux/of_mdio.h> |
afae5ad7 | 28 | #include <linux/of_device.h> |
1577ecef AF |
29 | |
30 | #include <asm/io.h> | |
9a4cbd53 | 31 | #if IS_ENABLED(CONFIG_UCC_GETH) |
1aa06d42 | 32 | #include <asm/ucc.h> /* for ucc_set_qe_mux_mii_mng() */ |
9a4cbd53 | 33 | #endif |
1577ecef AF |
34 | |
35 | #include "gianfar.h" | |
19bcd6c6 TT |
36 | |
37 | #define MIIMIND_BUSY 0x00000001 | |
38 | #define MIIMIND_NOTVALID 0x00000004 | |
39 | #define MIIMCFG_INIT_VALUE 0x00000007 | |
40 | #define MIIMCFG_RESET 0x80000000 | |
41 | ||
42 | #define MII_READ_COMMAND 0x00000001 | |
43 | ||
afae5ad7 TT |
44 | struct fsl_pq_mii { |
45 | u32 miimcfg; /* MII management configuration reg */ | |
46 | u32 miimcom; /* MII management command reg */ | |
47 | u32 miimadd; /* MII management address reg */ | |
48 | u32 miimcon; /* MII management control reg */ | |
49 | u32 miimstat; /* MII management status reg */ | |
50 | u32 miimind; /* MII management indication reg */ | |
51 | }; | |
52 | ||
19bcd6c6 TT |
53 | struct fsl_pq_mdio { |
54 | u8 res1[16]; | |
55 | u32 ieventm; /* MDIO Interrupt event register (for etsec2)*/ | |
56 | u32 imaskm; /* MDIO Interrupt mask register (for etsec2)*/ | |
57 | u8 res2[4]; | |
58 | u32 emapm; /* MDIO Event mapping register (for etsec2)*/ | |
59 | u8 res3[1280]; | |
afae5ad7 | 60 | struct fsl_pq_mii mii; |
19bcd6c6 TT |
61 | u8 res4[28]; |
62 | u32 utbipar; /* TBI phy address reg (only on UCC) */ | |
63 | u8 res5[2728]; | |
64 | } __packed; | |
1577ecef | 65 | |
59399c59 TT |
66 | /* Number of microseconds to wait for an MII register to respond */ |
67 | #define MII_TIMEOUT 1000 | |
68 | ||
b3319b10 AV |
69 | struct fsl_pq_mdio_priv { |
70 | void __iomem *map; | |
afae5ad7 | 71 | struct fsl_pq_mii __iomem *regs; |
dd3b8a32 | 72 | int irqs[PHY_MAX_ADDR]; |
afae5ad7 TT |
73 | }; |
74 | ||
75 | /* | |
76 | * Per-device-type data. Each type of device tree node that we support gets | |
77 | * one of these. | |
78 | * | |
79 | * @mii_offset: the offset of the MII registers within the memory map of the | |
80 | * node. Some nodes define only the MII registers, and some define the whole | |
81 | * MAC (which includes the MII registers). | |
82 | * | |
83 | * @get_tbipa: determines the address of the TBIPA register | |
84 | * | |
85 | * @ucc_configure: a special function for extra QE configuration | |
86 | */ | |
87 | struct fsl_pq_mdio_data { | |
88 | unsigned int mii_offset; /* offset of the MII registers */ | |
89 | uint32_t __iomem * (*get_tbipa)(void __iomem *p); | |
90 | void (*ucc_configure)(phys_addr_t start, phys_addr_t end); | |
b3319b10 AV |
91 | }; |
92 | ||
1577ecef | 93 | /* |
69cfb419 TT |
94 | * Write value to the PHY at mii_id at register regnum, on the bus attached |
95 | * to the local interface, which may be different from the generic mdio bus | |
96 | * (tied to a single interface), waiting until the write is done before | |
97 | * returning. This is helpful in programming interfaces like the TBI which | |
98 | * control interfaces like onchip SERDES and are always tied to the local | |
99 | * mdio pins, which may not be the same as system mdio bus, used for | |
1577ecef AF |
100 | * controlling the external PHYs, for example. |
101 | */ | |
69cfb419 TT |
102 | static int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, |
103 | u16 value) | |
1577ecef | 104 | { |
69cfb419 | 105 | struct fsl_pq_mdio_priv *priv = bus->priv; |
afae5ad7 | 106 | struct fsl_pq_mii __iomem *regs = priv->regs; |
e4b081f5 | 107 | unsigned int timeout; |
59399c59 | 108 | |
1577ecef | 109 | /* Set the PHY address and the register address we want to write */ |
f5bbd262 | 110 | iowrite32be((mii_id << 8) | regnum, ®s->miimadd); |
1577ecef AF |
111 | |
112 | /* Write out the value we want */ | |
f5bbd262 | 113 | iowrite32be(value, ®s->miimcon); |
1577ecef AF |
114 | |
115 | /* Wait for the transaction to finish */ | |
e4b081f5 CM |
116 | timeout = MII_TIMEOUT; |
117 | while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) { | |
118 | cpu_relax(); | |
119 | timeout--; | |
120 | } | |
1577ecef | 121 | |
e4b081f5 | 122 | return timeout ? 0 : -ETIMEDOUT; |
1577ecef AF |
123 | } |
124 | ||
125 | /* | |
69cfb419 TT |
126 | * Read the bus for PHY at addr mii_id, register regnum, and return the value. |
127 | * Clears miimcom first. | |
128 | * | |
129 | * All PHY operation done on the bus attached to the local interface, which | |
130 | * may be different from the generic mdio bus. This is helpful in programming | |
131 | * interfaces like the TBI which, in turn, control interfaces like on-chip | |
132 | * SERDES and are always tied to the local mdio pins, which may not be the | |
1577ecef AF |
133 | * same as system mdio bus, used for controlling the external PHYs, for eg. |
134 | */ | |
69cfb419 | 135 | static int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) |
1577ecef | 136 | { |
69cfb419 | 137 | struct fsl_pq_mdio_priv *priv = bus->priv; |
afae5ad7 | 138 | struct fsl_pq_mii __iomem *regs = priv->regs; |
e4b081f5 | 139 | unsigned int timeout; |
69cfb419 | 140 | u16 value; |
1577ecef AF |
141 | |
142 | /* Set the PHY address and the register address we want to read */ | |
f5bbd262 | 143 | iowrite32be((mii_id << 8) | regnum, ®s->miimadd); |
1577ecef AF |
144 | |
145 | /* Clear miimcom, and then initiate a read */ | |
f5bbd262 CM |
146 | iowrite32be(0, ®s->miimcom); |
147 | iowrite32be(MII_READ_COMMAND, ®s->miimcom); | |
1577ecef | 148 | |
59399c59 | 149 | /* Wait for the transaction to finish, normally less than 100us */ |
e4b081f5 CM |
150 | timeout = MII_TIMEOUT; |
151 | while ((ioread32be(®s->miimind) & | |
152 | (MIIMIND_NOTVALID | MIIMIND_BUSY)) && timeout) { | |
153 | cpu_relax(); | |
154 | timeout--; | |
155 | } | |
156 | ||
157 | if (!timeout) | |
59399c59 | 158 | return -ETIMEDOUT; |
1577ecef AF |
159 | |
160 | /* Grab the value of the register from miimstat */ | |
f5bbd262 | 161 | value = ioread32be(®s->miimstat); |
1577ecef | 162 | |
afae5ad7 | 163 | dev_dbg(&bus->dev, "read %04x from address %x/%x\n", value, mii_id, regnum); |
1577ecef AF |
164 | return value; |
165 | } | |
166 | ||
1577ecef AF |
167 | /* Reset the MIIM registers, and wait for the bus to free */ |
168 | static int fsl_pq_mdio_reset(struct mii_bus *bus) | |
169 | { | |
69cfb419 | 170 | struct fsl_pq_mdio_priv *priv = bus->priv; |
afae5ad7 | 171 | struct fsl_pq_mii __iomem *regs = priv->regs; |
e4b081f5 | 172 | unsigned int timeout; |
1577ecef AF |
173 | |
174 | mutex_lock(&bus->mdio_lock); | |
175 | ||
176 | /* Reset the management interface */ | |
f5bbd262 | 177 | iowrite32be(MIIMCFG_RESET, ®s->miimcfg); |
1577ecef AF |
178 | |
179 | /* Setup the MII Mgmt clock speed */ | |
f5bbd262 | 180 | iowrite32be(MIIMCFG_INIT_VALUE, ®s->miimcfg); |
1577ecef AF |
181 | |
182 | /* Wait until the bus is free */ | |
e4b081f5 CM |
183 | timeout = MII_TIMEOUT; |
184 | while ((ioread32be(®s->miimind) & MIIMIND_BUSY) && timeout) { | |
185 | cpu_relax(); | |
186 | timeout--; | |
187 | } | |
1577ecef AF |
188 | |
189 | mutex_unlock(&bus->mdio_lock); | |
190 | ||
e4b081f5 | 191 | if (!timeout) { |
5078ac79 | 192 | dev_err(&bus->dev, "timeout waiting for MII bus\n"); |
1577ecef AF |
193 | return -EBUSY; |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
952c5ca1 | 199 | #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) |
afae5ad7 TT |
200 | /* |
201 | * This is mildly evil, but so is our hardware for doing this. | |
202 | * Also, we have to cast back to struct gfar because of | |
203 | * definition weirdness done in gianfar.h. | |
204 | */ | |
205 | static uint32_t __iomem *get_gfar_tbipa(void __iomem *p) | |
206 | { | |
207 | struct gfar __iomem *enet_regs = p; | |
1577ecef | 208 | |
afae5ad7 | 209 | return &enet_regs->tbipa; |
952c5ca1 | 210 | } |
1577ecef | 211 | |
afae5ad7 TT |
212 | /* |
213 | * Return the TBIPAR address for an eTSEC2 node | |
214 | */ | |
215 | static uint32_t __iomem *get_etsec_tbipa(void __iomem *p) | |
1577ecef | 216 | { |
afae5ad7 TT |
217 | return p; |
218 | } | |
219 | #endif | |
220 | ||
952c5ca1 | 221 | #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) |
afae5ad7 TT |
222 | /* |
223 | * Return the TBIPAR address for a QE MDIO node | |
224 | */ | |
225 | static uint32_t __iomem *get_ucc_tbipa(void __iomem *p) | |
226 | { | |
227 | struct fsl_pq_mdio __iomem *mdio = p; | |
228 | ||
229 | return &mdio->utbipar; | |
230 | } | |
231 | ||
232 | /* | |
233 | * Find the UCC node that controls the given MDIO node | |
234 | * | |
235 | * For some reason, the QE MDIO nodes are not children of the UCC devices | |
236 | * that control them. Therefore, we need to scan all UCC nodes looking for | |
237 | * the one that encompases the given MDIO node. We do this by comparing | |
238 | * physical addresses. The 'start' and 'end' addresses of the MDIO node are | |
239 | * passed, and the correct UCC node will cover the entire address range. | |
240 | * | |
241 | * This assumes that there is only one QE MDIO node in the entire device tree. | |
242 | */ | |
243 | static void ucc_configure(phys_addr_t start, phys_addr_t end) | |
244 | { | |
245 | static bool found_mii_master; | |
1577ecef | 246 | struct device_node *np = NULL; |
1577ecef | 247 | |
afae5ad7 TT |
248 | if (found_mii_master) |
249 | return; | |
1577ecef | 250 | |
afae5ad7 TT |
251 | for_each_compatible_node(np, NULL, "ucc_geth") { |
252 | struct resource res; | |
253 | const uint32_t *iprop; | |
254 | uint32_t id; | |
255 | int ret; | |
256 | ||
257 | ret = of_address_to_resource(np, 0, &res); | |
258 | if (ret < 0) { | |
259 | pr_debug("fsl-pq-mdio: no address range in node %s\n", | |
260 | np->full_name); | |
1577ecef | 261 | continue; |
afae5ad7 | 262 | } |
1577ecef AF |
263 | |
264 | /* if our mdio regs fall within this UCC regs range */ | |
afae5ad7 TT |
265 | if ((start < res.start) || (end > res.end)) |
266 | continue; | |
267 | ||
268 | iprop = of_get_property(np, "cell-index", NULL); | |
269 | if (!iprop) { | |
270 | iprop = of_get_property(np, "device-id", NULL); | |
271 | if (!iprop) { | |
272 | pr_debug("fsl-pq-mdio: no UCC ID in node %s\n", | |
273 | np->full_name); | |
274 | continue; | |
1577ecef | 275 | } |
afae5ad7 | 276 | } |
1577ecef | 277 | |
afae5ad7 | 278 | id = be32_to_cpup(iprop); |
1577ecef | 279 | |
afae5ad7 TT |
280 | /* |
281 | * cell-index and device-id for QE nodes are | |
282 | * numbered from 1, not 0. | |
283 | */ | |
284 | if (ucc_set_qe_mux_mii_mng(id - 1) < 0) { | |
285 | pr_debug("fsl-pq-mdio: invalid UCC ID in node %s\n", | |
286 | np->full_name); | |
287 | continue; | |
1577ecef | 288 | } |
afae5ad7 TT |
289 | |
290 | pr_debug("fsl-pq-mdio: setting node UCC%u to MII master\n", id); | |
291 | found_mii_master = true; | |
1577ecef | 292 | } |
afae5ad7 | 293 | } |
1577ecef | 294 | |
1577ecef | 295 | #endif |
afae5ad7 | 296 | |
94e5a2a8 | 297 | static const struct of_device_id fsl_pq_mdio_match[] = { |
afae5ad7 TT |
298 | #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) |
299 | { | |
300 | .compatible = "fsl,gianfar-tbi", | |
301 | .data = &(struct fsl_pq_mdio_data) { | |
302 | .mii_offset = 0, | |
303 | .get_tbipa = get_gfar_tbipa, | |
304 | }, | |
305 | }, | |
306 | { | |
307 | .compatible = "fsl,gianfar-mdio", | |
308 | .data = &(struct fsl_pq_mdio_data) { | |
309 | .mii_offset = 0, | |
310 | .get_tbipa = get_gfar_tbipa, | |
311 | }, | |
312 | }, | |
313 | { | |
314 | .type = "mdio", | |
315 | .compatible = "gianfar", | |
316 | .data = &(struct fsl_pq_mdio_data) { | |
317 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
318 | .get_tbipa = get_gfar_tbipa, | |
319 | }, | |
320 | }, | |
321 | { | |
322 | .compatible = "fsl,etsec2-tbi", | |
323 | .data = &(struct fsl_pq_mdio_data) { | |
324 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
325 | .get_tbipa = get_etsec_tbipa, | |
326 | }, | |
327 | }, | |
328 | { | |
329 | .compatible = "fsl,etsec2-mdio", | |
330 | .data = &(struct fsl_pq_mdio_data) { | |
331 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
332 | .get_tbipa = get_etsec_tbipa, | |
333 | }, | |
334 | }, | |
335 | #endif | |
336 | #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) | |
337 | { | |
338 | .compatible = "fsl,ucc-mdio", | |
339 | .data = &(struct fsl_pq_mdio_data) { | |
340 | .mii_offset = 0, | |
341 | .get_tbipa = get_ucc_tbipa, | |
342 | .ucc_configure = ucc_configure, | |
343 | }, | |
344 | }, | |
345 | { | |
346 | /* Legacy UCC MDIO node */ | |
347 | .type = "mdio", | |
348 | .compatible = "ucc_geth_phy", | |
349 | .data = &(struct fsl_pq_mdio_data) { | |
350 | .mii_offset = 0, | |
351 | .get_tbipa = get_ucc_tbipa, | |
352 | .ucc_configure = ucc_configure, | |
353 | }, | |
354 | }, | |
355 | #endif | |
761743eb TT |
356 | /* No Kconfig option for Fman support yet */ |
357 | { | |
358 | .compatible = "fsl,fman-mdio", | |
359 | .data = &(struct fsl_pq_mdio_data) { | |
360 | .mii_offset = 0, | |
361 | /* Fman TBI operations are handled elsewhere */ | |
362 | }, | |
363 | }, | |
364 | ||
afae5ad7 TT |
365 | {}, |
366 | }; | |
367 | MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); | |
1577ecef | 368 | |
5078ac79 | 369 | static int fsl_pq_mdio_probe(struct platform_device *pdev) |
1577ecef | 370 | { |
afae5ad7 TT |
371 | const struct of_device_id *id = |
372 | of_match_device(fsl_pq_mdio_match, &pdev->dev); | |
373 | const struct fsl_pq_mdio_data *data = id->data; | |
5078ac79 | 374 | struct device_node *np = pdev->dev.of_node; |
afae5ad7 | 375 | struct resource res; |
1577ecef | 376 | struct device_node *tbi; |
b3319b10 | 377 | struct fsl_pq_mdio_priv *priv; |
1577ecef | 378 | struct mii_bus *new_bus; |
08d18f3b | 379 | int err; |
1577ecef | 380 | |
afae5ad7 TT |
381 | dev_dbg(&pdev->dev, "found %s compatible node\n", id->compatible); |
382 | ||
dd3b8a32 TT |
383 | new_bus = mdiobus_alloc_size(sizeof(*priv)); |
384 | if (!new_bus) | |
b3319b10 AV |
385 | return -ENOMEM; |
386 | ||
dd3b8a32 | 387 | priv = new_bus->priv; |
1577ecef | 388 | new_bus->name = "Freescale PowerQUICC MII Bus", |
5078ac79 TT |
389 | new_bus->read = &fsl_pq_mdio_read; |
390 | new_bus->write = &fsl_pq_mdio_write; | |
391 | new_bus->reset = &fsl_pq_mdio_reset; | |
dd3b8a32 | 392 | new_bus->irq = priv->irqs; |
1577ecef | 393 | |
afae5ad7 TT |
394 | err = of_address_to_resource(np, 0, &res); |
395 | if (err < 0) { | |
396 | dev_err(&pdev->dev, "could not obtain address information\n"); | |
dd3b8a32 | 397 | goto error; |
3b1fd3e5 AV |
398 | } |
399 | ||
69cfb419 | 400 | snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s@%llx", np->name, |
afae5ad7 | 401 | (unsigned long long)res.start); |
69cfb419 | 402 | |
afae5ad7 TT |
403 | priv->map = of_iomap(np, 0); |
404 | if (!priv->map) { | |
1577ecef | 405 | err = -ENOMEM; |
dd3b8a32 | 406 | goto error; |
1577ecef AF |
407 | } |
408 | ||
afae5ad7 TT |
409 | /* |
410 | * Some device tree nodes represent only the MII registers, and | |
411 | * others represent the MAC and MII registers. The 'mii_offset' field | |
412 | * contains the offset of the MII registers inside the mapped register | |
413 | * space. | |
414 | */ | |
415 | if (data->mii_offset > resource_size(&res)) { | |
416 | dev_err(&pdev->dev, "invalid register map\n"); | |
417 | err = -EINVAL; | |
dd3b8a32 | 418 | goto error; |
afae5ad7 TT |
419 | } |
420 | priv->regs = priv->map + data->mii_offset; | |
1577ecef | 421 | |
5078ac79 | 422 | new_bus->parent = &pdev->dev; |
a0e18600 | 423 | platform_set_drvdata(pdev, new_bus); |
1577ecef | 424 | |
afae5ad7 TT |
425 | if (data->get_tbipa) { |
426 | for_each_child_of_node(np, tbi) { | |
427 | if (strcmp(tbi->type, "tbi-phy") == 0) { | |
428 | dev_dbg(&pdev->dev, "found TBI PHY node %s\n", | |
429 | strrchr(tbi->full_name, '/') + 1); | |
430 | break; | |
431 | } | |
fbcc0e2c | 432 | } |
1577ecef | 433 | |
afae5ad7 TT |
434 | if (tbi) { |
435 | const u32 *prop = of_get_property(tbi, "reg", NULL); | |
436 | uint32_t __iomem *tbipa; | |
1577ecef | 437 | |
afae5ad7 TT |
438 | if (!prop) { |
439 | dev_err(&pdev->dev, | |
440 | "missing 'reg' property in node %s\n", | |
441 | tbi->full_name); | |
442 | err = -EBUSY; | |
443 | goto error; | |
444 | } | |
1577ecef | 445 | |
afae5ad7 | 446 | tbipa = data->get_tbipa(priv->map); |
1577ecef | 447 | |
f5bbd262 | 448 | iowrite32be(be32_to_cpup(prop), tbipa); |
464b57da | 449 | } |
1577ecef AF |
450 | } |
451 | ||
afae5ad7 TT |
452 | if (data->ucc_configure) |
453 | data->ucc_configure(res.start, res.end); | |
454 | ||
324931ba | 455 | err = of_mdiobus_register(new_bus, np); |
1577ecef | 456 | if (err) { |
5078ac79 TT |
457 | dev_err(&pdev->dev, "cannot register %s as MDIO bus\n", |
458 | new_bus->name); | |
dd3b8a32 | 459 | goto error; |
1577ecef AF |
460 | } |
461 | ||
462 | return 0; | |
463 | ||
dd3b8a32 TT |
464 | error: |
465 | if (priv->map) | |
466 | iounmap(priv->map); | |
467 | ||
1577ecef | 468 | kfree(new_bus); |
dd3b8a32 | 469 | |
1577ecef AF |
470 | return err; |
471 | } | |
472 | ||
473 | ||
5078ac79 | 474 | static int fsl_pq_mdio_remove(struct platform_device *pdev) |
1577ecef | 475 | { |
5078ac79 | 476 | struct device *device = &pdev->dev; |
1577ecef | 477 | struct mii_bus *bus = dev_get_drvdata(device); |
b3319b10 | 478 | struct fsl_pq_mdio_priv *priv = bus->priv; |
1577ecef AF |
479 | |
480 | mdiobus_unregister(bus); | |
481 | ||
b3319b10 | 482 | iounmap(priv->map); |
1577ecef AF |
483 | mdiobus_free(bus); |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
74888760 | 488 | static struct platform_driver fsl_pq_mdio_driver = { |
4018294b GL |
489 | .driver = { |
490 | .name = "fsl-pq_mdio", | |
4018294b GL |
491 | .of_match_table = fsl_pq_mdio_match, |
492 | }, | |
1577ecef AF |
493 | .probe = fsl_pq_mdio_probe, |
494 | .remove = fsl_pq_mdio_remove, | |
1577ecef AF |
495 | }; |
496 | ||
db62f684 | 497 | module_platform_driver(fsl_pq_mdio_driver); |
1577ecef | 498 | |
26062897 | 499 | MODULE_LICENSE("GPL"); |