]>
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 | 200 | /* |
3bb35ac4 GF |
201 | * Return the TBIPA address, starting from the address |
202 | * of the mapped GFAR MDIO registers (struct gfar) | |
afae5ad7 TT |
203 | * This is mildly evil, but so is our hardware for doing this. |
204 | * Also, we have to cast back to struct gfar because of | |
205 | * definition weirdness done in gianfar.h. | |
206 | */ | |
3bb35ac4 | 207 | static uint32_t __iomem *get_gfar_tbipa_from_mdio(void __iomem *p) |
afae5ad7 TT |
208 | { |
209 | struct gfar __iomem *enet_regs = p; | |
1577ecef | 210 | |
afae5ad7 | 211 | return &enet_regs->tbipa; |
952c5ca1 | 212 | } |
1577ecef | 213 | |
3bb35ac4 GF |
214 | /* |
215 | * Return the TBIPA address, starting from the address | |
216 | * of the mapped GFAR MII registers (gfar_mii_regs[] within struct gfar) | |
217 | */ | |
218 | static uint32_t __iomem *get_gfar_tbipa_from_mii(void __iomem *p) | |
219 | { | |
220 | return get_gfar_tbipa_from_mdio(container_of(p, struct gfar, gfar_mii_regs)); | |
221 | } | |
222 | ||
afae5ad7 TT |
223 | /* |
224 | * Return the TBIPAR address for an eTSEC2 node | |
225 | */ | |
226 | static uint32_t __iomem *get_etsec_tbipa(void __iomem *p) | |
1577ecef | 227 | { |
afae5ad7 TT |
228 | return p; |
229 | } | |
230 | #endif | |
231 | ||
952c5ca1 | 232 | #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) |
afae5ad7 | 233 | /* |
3bb35ac4 GF |
234 | * Return the TBIPAR address for a QE MDIO node, starting from the address |
235 | * of the mapped MII registers (struct fsl_pq_mii) | |
afae5ad7 TT |
236 | */ |
237 | static uint32_t __iomem *get_ucc_tbipa(void __iomem *p) | |
238 | { | |
3bb35ac4 | 239 | struct fsl_pq_mdio __iomem *mdio = container_of(p, struct fsl_pq_mdio, mii); |
afae5ad7 TT |
240 | |
241 | return &mdio->utbipar; | |
242 | } | |
243 | ||
244 | /* | |
245 | * Find the UCC node that controls the given MDIO node | |
246 | * | |
247 | * For some reason, the QE MDIO nodes are not children of the UCC devices | |
248 | * that control them. Therefore, we need to scan all UCC nodes looking for | |
249 | * the one that encompases the given MDIO node. We do this by comparing | |
250 | * physical addresses. The 'start' and 'end' addresses of the MDIO node are | |
251 | * passed, and the correct UCC node will cover the entire address range. | |
252 | * | |
253 | * This assumes that there is only one QE MDIO node in the entire device tree. | |
254 | */ | |
255 | static void ucc_configure(phys_addr_t start, phys_addr_t end) | |
256 | { | |
257 | static bool found_mii_master; | |
1577ecef | 258 | struct device_node *np = NULL; |
1577ecef | 259 | |
afae5ad7 TT |
260 | if (found_mii_master) |
261 | return; | |
1577ecef | 262 | |
afae5ad7 TT |
263 | for_each_compatible_node(np, NULL, "ucc_geth") { |
264 | struct resource res; | |
265 | const uint32_t *iprop; | |
266 | uint32_t id; | |
267 | int ret; | |
268 | ||
269 | ret = of_address_to_resource(np, 0, &res); | |
270 | if (ret < 0) { | |
271 | pr_debug("fsl-pq-mdio: no address range in node %s\n", | |
272 | np->full_name); | |
1577ecef | 273 | continue; |
afae5ad7 | 274 | } |
1577ecef AF |
275 | |
276 | /* if our mdio regs fall within this UCC regs range */ | |
afae5ad7 TT |
277 | if ((start < res.start) || (end > res.end)) |
278 | continue; | |
279 | ||
280 | iprop = of_get_property(np, "cell-index", NULL); | |
281 | if (!iprop) { | |
282 | iprop = of_get_property(np, "device-id", NULL); | |
283 | if (!iprop) { | |
284 | pr_debug("fsl-pq-mdio: no UCC ID in node %s\n", | |
285 | np->full_name); | |
286 | continue; | |
1577ecef | 287 | } |
afae5ad7 | 288 | } |
1577ecef | 289 | |
afae5ad7 | 290 | id = be32_to_cpup(iprop); |
1577ecef | 291 | |
afae5ad7 TT |
292 | /* |
293 | * cell-index and device-id for QE nodes are | |
294 | * numbered from 1, not 0. | |
295 | */ | |
296 | if (ucc_set_qe_mux_mii_mng(id - 1) < 0) { | |
297 | pr_debug("fsl-pq-mdio: invalid UCC ID in node %s\n", | |
298 | np->full_name); | |
299 | continue; | |
1577ecef | 300 | } |
afae5ad7 TT |
301 | |
302 | pr_debug("fsl-pq-mdio: setting node UCC%u to MII master\n", id); | |
303 | found_mii_master = true; | |
1577ecef | 304 | } |
afae5ad7 | 305 | } |
1577ecef | 306 | |
1577ecef | 307 | #endif |
afae5ad7 | 308 | |
94e5a2a8 | 309 | static const struct of_device_id fsl_pq_mdio_match[] = { |
afae5ad7 TT |
310 | #if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE) |
311 | { | |
312 | .compatible = "fsl,gianfar-tbi", | |
313 | .data = &(struct fsl_pq_mdio_data) { | |
314 | .mii_offset = 0, | |
3bb35ac4 | 315 | .get_tbipa = get_gfar_tbipa_from_mii, |
afae5ad7 TT |
316 | }, |
317 | }, | |
318 | { | |
319 | .compatible = "fsl,gianfar-mdio", | |
320 | .data = &(struct fsl_pq_mdio_data) { | |
321 | .mii_offset = 0, | |
3bb35ac4 | 322 | .get_tbipa = get_gfar_tbipa_from_mii, |
afae5ad7 TT |
323 | }, |
324 | }, | |
325 | { | |
326 | .type = "mdio", | |
327 | .compatible = "gianfar", | |
328 | .data = &(struct fsl_pq_mdio_data) { | |
329 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
3bb35ac4 | 330 | .get_tbipa = get_gfar_tbipa_from_mdio, |
afae5ad7 TT |
331 | }, |
332 | }, | |
333 | { | |
334 | .compatible = "fsl,etsec2-tbi", | |
335 | .data = &(struct fsl_pq_mdio_data) { | |
336 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
337 | .get_tbipa = get_etsec_tbipa, | |
338 | }, | |
339 | }, | |
340 | { | |
341 | .compatible = "fsl,etsec2-mdio", | |
342 | .data = &(struct fsl_pq_mdio_data) { | |
343 | .mii_offset = offsetof(struct fsl_pq_mdio, mii), | |
344 | .get_tbipa = get_etsec_tbipa, | |
345 | }, | |
346 | }, | |
347 | #endif | |
348 | #if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE) | |
349 | { | |
350 | .compatible = "fsl,ucc-mdio", | |
351 | .data = &(struct fsl_pq_mdio_data) { | |
352 | .mii_offset = 0, | |
353 | .get_tbipa = get_ucc_tbipa, | |
354 | .ucc_configure = ucc_configure, | |
355 | }, | |
356 | }, | |
357 | { | |
358 | /* Legacy UCC MDIO node */ | |
359 | .type = "mdio", | |
360 | .compatible = "ucc_geth_phy", | |
361 | .data = &(struct fsl_pq_mdio_data) { | |
362 | .mii_offset = 0, | |
363 | .get_tbipa = get_ucc_tbipa, | |
364 | .ucc_configure = ucc_configure, | |
365 | }, | |
366 | }, | |
367 | #endif | |
761743eb TT |
368 | /* No Kconfig option for Fman support yet */ |
369 | { | |
370 | .compatible = "fsl,fman-mdio", | |
371 | .data = &(struct fsl_pq_mdio_data) { | |
372 | .mii_offset = 0, | |
373 | /* Fman TBI operations are handled elsewhere */ | |
374 | }, | |
375 | }, | |
376 | ||
afae5ad7 TT |
377 | {}, |
378 | }; | |
379 | MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); | |
1577ecef | 380 | |
5078ac79 | 381 | static int fsl_pq_mdio_probe(struct platform_device *pdev) |
1577ecef | 382 | { |
afae5ad7 TT |
383 | const struct of_device_id *id = |
384 | of_match_device(fsl_pq_mdio_match, &pdev->dev); | |
385 | const struct fsl_pq_mdio_data *data = id->data; | |
5078ac79 | 386 | struct device_node *np = pdev->dev.of_node; |
afae5ad7 | 387 | struct resource res; |
1577ecef | 388 | struct device_node *tbi; |
b3319b10 | 389 | struct fsl_pq_mdio_priv *priv; |
1577ecef | 390 | struct mii_bus *new_bus; |
08d18f3b | 391 | int err; |
1577ecef | 392 | |
afae5ad7 TT |
393 | dev_dbg(&pdev->dev, "found %s compatible node\n", id->compatible); |
394 | ||
dd3b8a32 TT |
395 | new_bus = mdiobus_alloc_size(sizeof(*priv)); |
396 | if (!new_bus) | |
b3319b10 AV |
397 | return -ENOMEM; |
398 | ||
dd3b8a32 | 399 | priv = new_bus->priv; |
1577ecef | 400 | new_bus->name = "Freescale PowerQUICC MII Bus", |
5078ac79 TT |
401 | new_bus->read = &fsl_pq_mdio_read; |
402 | new_bus->write = &fsl_pq_mdio_write; | |
403 | new_bus->reset = &fsl_pq_mdio_reset; | |
dd3b8a32 | 404 | new_bus->irq = priv->irqs; |
1577ecef | 405 | |
afae5ad7 TT |
406 | err = of_address_to_resource(np, 0, &res); |
407 | if (err < 0) { | |
408 | dev_err(&pdev->dev, "could not obtain address information\n"); | |
dd3b8a32 | 409 | goto error; |
3b1fd3e5 AV |
410 | } |
411 | ||
69cfb419 | 412 | snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s@%llx", np->name, |
afae5ad7 | 413 | (unsigned long long)res.start); |
69cfb419 | 414 | |
afae5ad7 TT |
415 | priv->map = of_iomap(np, 0); |
416 | if (!priv->map) { | |
1577ecef | 417 | err = -ENOMEM; |
dd3b8a32 | 418 | goto error; |
1577ecef AF |
419 | } |
420 | ||
afae5ad7 TT |
421 | /* |
422 | * Some device tree nodes represent only the MII registers, and | |
423 | * others represent the MAC and MII registers. The 'mii_offset' field | |
424 | * contains the offset of the MII registers inside the mapped register | |
425 | * space. | |
426 | */ | |
427 | if (data->mii_offset > resource_size(&res)) { | |
428 | dev_err(&pdev->dev, "invalid register map\n"); | |
429 | err = -EINVAL; | |
dd3b8a32 | 430 | goto error; |
afae5ad7 TT |
431 | } |
432 | priv->regs = priv->map + data->mii_offset; | |
1577ecef | 433 | |
5078ac79 | 434 | new_bus->parent = &pdev->dev; |
a0e18600 | 435 | platform_set_drvdata(pdev, new_bus); |
1577ecef | 436 | |
afae5ad7 TT |
437 | if (data->get_tbipa) { |
438 | for_each_child_of_node(np, tbi) { | |
439 | if (strcmp(tbi->type, "tbi-phy") == 0) { | |
440 | dev_dbg(&pdev->dev, "found TBI PHY node %s\n", | |
441 | strrchr(tbi->full_name, '/') + 1); | |
442 | break; | |
443 | } | |
fbcc0e2c | 444 | } |
1577ecef | 445 | |
afae5ad7 TT |
446 | if (tbi) { |
447 | const u32 *prop = of_get_property(tbi, "reg", NULL); | |
448 | uint32_t __iomem *tbipa; | |
1577ecef | 449 | |
afae5ad7 TT |
450 | if (!prop) { |
451 | dev_err(&pdev->dev, | |
452 | "missing 'reg' property in node %s\n", | |
453 | tbi->full_name); | |
454 | err = -EBUSY; | |
455 | goto error; | |
456 | } | |
1577ecef | 457 | |
afae5ad7 | 458 | tbipa = data->get_tbipa(priv->map); |
1577ecef | 459 | |
3dd03e52 GF |
460 | /* |
461 | * Add consistency check to make sure TBI is contained | |
462 | * within the mapped range (not because we would get a | |
463 | * segfault, rather to catch bugs in computing TBI | |
464 | * address). Print error message but continue anyway. | |
465 | */ | |
466 | if ((void *)tbipa > priv->map + resource_size(&res) - 4) | |
8cde3e44 | 467 | dev_err(&pdev->dev, "invalid register map (should be at least 0x%04zx to contain TBI address)\n", |
3dd03e52 GF |
468 | ((void *)tbipa - priv->map) + 4); |
469 | ||
f5bbd262 | 470 | iowrite32be(be32_to_cpup(prop), tbipa); |
464b57da | 471 | } |
1577ecef AF |
472 | } |
473 | ||
afae5ad7 TT |
474 | if (data->ucc_configure) |
475 | data->ucc_configure(res.start, res.end); | |
476 | ||
324931ba | 477 | err = of_mdiobus_register(new_bus, np); |
1577ecef | 478 | if (err) { |
5078ac79 TT |
479 | dev_err(&pdev->dev, "cannot register %s as MDIO bus\n", |
480 | new_bus->name); | |
dd3b8a32 | 481 | goto error; |
1577ecef AF |
482 | } |
483 | ||
484 | return 0; | |
485 | ||
dd3b8a32 TT |
486 | error: |
487 | if (priv->map) | |
488 | iounmap(priv->map); | |
489 | ||
1577ecef | 490 | kfree(new_bus); |
dd3b8a32 | 491 | |
1577ecef AF |
492 | return err; |
493 | } | |
494 | ||
495 | ||
5078ac79 | 496 | static int fsl_pq_mdio_remove(struct platform_device *pdev) |
1577ecef | 497 | { |
5078ac79 | 498 | struct device *device = &pdev->dev; |
1577ecef | 499 | struct mii_bus *bus = dev_get_drvdata(device); |
b3319b10 | 500 | struct fsl_pq_mdio_priv *priv = bus->priv; |
1577ecef AF |
501 | |
502 | mdiobus_unregister(bus); | |
503 | ||
b3319b10 | 504 | iounmap(priv->map); |
1577ecef AF |
505 | mdiobus_free(bus); |
506 | ||
507 | return 0; | |
508 | } | |
509 | ||
74888760 | 510 | static struct platform_driver fsl_pq_mdio_driver = { |
4018294b GL |
511 | .driver = { |
512 | .name = "fsl-pq_mdio", | |
4018294b GL |
513 | .of_match_table = fsl_pq_mdio_match, |
514 | }, | |
1577ecef AF |
515 | .probe = fsl_pq_mdio_probe, |
516 | .remove = fsl_pq_mdio_remove, | |
1577ecef AF |
517 | }; |
518 | ||
db62f684 | 519 | module_platform_driver(fsl_pq_mdio_driver); |
1577ecef | 520 | |
26062897 | 521 | MODULE_LICENSE("GPL"); |