Commit b3319b10523d8dac82b134a05de2a403119abebd

Authored by Anton Vorontsov
Committed by David S. Miller
1 parent 29fb00e047

fsl_pq_mdio: Fix iomem unmapping for non-eTSEC2.0 controllers

We use a rather complicated logic to support eTSEC and eTSEC2.0
registers maps in a single driver. Currently, the code tries to
unmap 'regs', but for non-eTSEC2.0 controllers 'regs' doesn't
point to a mapping start, and this might cause badness on probe
failure or module removal:

 Freescale PowerQUICC MII Bus: probed
 Trying to vfree() nonexistent vm area (e107f000)
 ------------[ cut here ]------------
 Badness at c00a7754 [verbose debug info unavailable]
 NIP: c00a7754 LR: c00a7754 CTR: c02231ec
 [...]
 NIP [c00a7754] __vunmap+0xec/0xf4
 LR [c00a7754] __vunmap+0xec/0xf4
 Call Trace:
 [df827e50] [c00a7754] __vunmap+0xec/0xf4 (unreliable)
 [df827e70] [c001519c] iounmap+0x44/0x54
 [df827e80] [c028b924] fsl_pq_mdio_probe+0x1cc/0x2fc
 [df827eb0] [c02fb9b4] of_platform_device_probe+0x5c/0x84
 [df827ed0] [c0229928] really_probe+0x78/0x1a8
 [df827ef0] [c0229b20] __driver_attach+0xa4/0xa8

Fix this by introducing a proper priv structure (finally!), which
now holds 'regs' and 'map' fields separately.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 23 additions and 7 deletions Side-by-side Diff

drivers/net/fsl_pq_mdio.c
... ... @@ -46,6 +46,11 @@
46 46 #include "gianfar.h"
47 47 #include "fsl_pq_mdio.h"
48 48  
  49 +struct fsl_pq_mdio_priv {
  50 + void __iomem *map;
  51 + struct fsl_pq_mdio __iomem *regs;
  52 +};
  53 +
49 54 /*
50 55 * Write value to the PHY at mii_id at register regnum,
51 56 * on the bus attached to the local interface, which may be different from the
... ... @@ -105,7 +110,9 @@
105 110  
106 111 static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
107 112 {
108   - return (void __iomem __force *)bus->priv;
  113 + struct fsl_pq_mdio_priv *priv = bus->priv;
  114 +
  115 + return priv->regs;
109 116 }
110 117  
111 118 /*
... ... @@ -266,6 +273,7 @@
266 273 {
267 274 struct device_node *np = ofdev->node;
268 275 struct device_node *tbi;
  276 + struct fsl_pq_mdio_priv *priv;
269 277 struct fsl_pq_mdio __iomem *regs = NULL;
270 278 void __iomem *map;
271 279 u32 __iomem *tbipa;
272 280  
273 281  
... ... @@ -274,14 +282,19 @@
274 282 u64 addr = 0, size = 0;
275 283 int err = 0;
276 284  
  285 + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  286 + if (!priv)
  287 + return -ENOMEM;
  288 +
277 289 new_bus = mdiobus_alloc();
278 290 if (NULL == new_bus)
279   - return -ENOMEM;
  291 + goto err_free_priv;
280 292  
281 293 new_bus->name = "Freescale PowerQUICC MII Bus",
282 294 new_bus->read = &fsl_pq_mdio_read,
283 295 new_bus->write = &fsl_pq_mdio_write,
284 296 new_bus->reset = &fsl_pq_mdio_reset,
  297 + new_bus->priv = priv;
285 298 fsl_pq_mdio_bus_name(new_bus->id, np);
286 299  
287 300 /* Set the PHY base address */
... ... @@ -291,6 +304,7 @@
291 304 err = -ENOMEM;
292 305 goto err_free_bus;
293 306 }
  307 + priv->map = map;
294 308  
295 309 if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
296 310 of_device_is_compatible(np, "fsl,gianfar-tbi") ||
297 311  
... ... @@ -298,9 +312,8 @@
298 312 of_device_is_compatible(np, "ucc_geth_phy"))
299 313 map -= offsetof(struct fsl_pq_mdio, miimcfg);
300 314 regs = map;
  315 + priv->regs = regs;
301 316  
302   - new_bus->priv = (void __force *)regs;
303   -
304 317 new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
305 318  
306 319 if (NULL == new_bus->irq) {
307 320  
... ... @@ -392,10 +405,11 @@
392 405 err_free_irqs:
393 406 kfree(new_bus->irq);
394 407 err_unmap_regs:
395   - iounmap(regs);
  408 + iounmap(priv->map);
396 409 err_free_bus:
397 410 kfree(new_bus);
398   -
  411 +err_free_priv:
  412 + kfree(priv);
399 413 return err;
400 414 }
401 415  
402 416  
403 417  
... ... @@ -404,14 +418,16 @@
404 418 {
405 419 struct device *device = &ofdev->dev;
406 420 struct mii_bus *bus = dev_get_drvdata(device);
  421 + struct fsl_pq_mdio_priv *priv = bus->priv;
407 422  
408 423 mdiobus_unregister(bus);
409 424  
410 425 dev_set_drvdata(device, NULL);
411 426  
412   - iounmap(fsl_pq_mdio_get_regs(bus));
  427 + iounmap(priv->map);
413 428 bus->priv = NULL;
414 429 mdiobus_free(bus);
  430 + kfree(priv);
415 431  
416 432 return 0;
417 433 }