Commit b3319b10 authored by Anton Vorontsov's avatar Anton Vorontsov Committed by David S. Miller

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: default avatarAnton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 29fb00e0
...@@ -46,6 +46,11 @@ ...@@ -46,6 +46,11 @@
#include "gianfar.h" #include "gianfar.h"
#include "fsl_pq_mdio.h" #include "fsl_pq_mdio.h"
struct fsl_pq_mdio_priv {
void __iomem *map;
struct fsl_pq_mdio __iomem *regs;
};
/* /*
* Write value to the PHY at mii_id at register regnum, * Write value to the PHY at mii_id at register regnum,
* on the bus attached to the local interface, which may be different from the * on the bus attached to the local interface, which may be different from the
...@@ -105,7 +110,9 @@ int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs, ...@@ -105,7 +110,9 @@ int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs,
static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus) static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
{ {
return (void __iomem __force *)bus->priv; struct fsl_pq_mdio_priv *priv = bus->priv;
return priv->regs;
} }
/* /*
...@@ -266,6 +273,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, ...@@ -266,6 +273,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
{ {
struct device_node *np = ofdev->node; struct device_node *np = ofdev->node;
struct device_node *tbi; struct device_node *tbi;
struct fsl_pq_mdio_priv *priv;
struct fsl_pq_mdio __iomem *regs = NULL; struct fsl_pq_mdio __iomem *regs = NULL;
void __iomem *map; void __iomem *map;
u32 __iomem *tbipa; u32 __iomem *tbipa;
...@@ -274,14 +282,19 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, ...@@ -274,14 +282,19 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
u64 addr = 0, size = 0; u64 addr = 0, size = 0;
int err = 0; int err = 0;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
new_bus = mdiobus_alloc(); new_bus = mdiobus_alloc();
if (NULL == new_bus) if (NULL == new_bus)
return -ENOMEM; goto err_free_priv;
new_bus->name = "Freescale PowerQUICC MII Bus", new_bus->name = "Freescale PowerQUICC MII Bus",
new_bus->read = &fsl_pq_mdio_read, new_bus->read = &fsl_pq_mdio_read,
new_bus->write = &fsl_pq_mdio_write, new_bus->write = &fsl_pq_mdio_write,
new_bus->reset = &fsl_pq_mdio_reset, new_bus->reset = &fsl_pq_mdio_reset,
new_bus->priv = priv;
fsl_pq_mdio_bus_name(new_bus->id, np); fsl_pq_mdio_bus_name(new_bus->id, np);
/* Set the PHY base address */ /* Set the PHY base address */
...@@ -291,6 +304,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, ...@@ -291,6 +304,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
err = -ENOMEM; err = -ENOMEM;
goto err_free_bus; goto err_free_bus;
} }
priv->map = map;
if (of_device_is_compatible(np, "fsl,gianfar-mdio") || if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
of_device_is_compatible(np, "fsl,gianfar-tbi") || of_device_is_compatible(np, "fsl,gianfar-tbi") ||
...@@ -298,8 +312,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, ...@@ -298,8 +312,7 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
of_device_is_compatible(np, "ucc_geth_phy")) of_device_is_compatible(np, "ucc_geth_phy"))
map -= offsetof(struct fsl_pq_mdio, miimcfg); map -= offsetof(struct fsl_pq_mdio, miimcfg);
regs = map; regs = map;
priv->regs = regs;
new_bus->priv = (void __force *)regs;
new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
...@@ -392,10 +405,11 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev, ...@@ -392,10 +405,11 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
err_free_irqs: err_free_irqs:
kfree(new_bus->irq); kfree(new_bus->irq);
err_unmap_regs: err_unmap_regs:
iounmap(regs); iounmap(priv->map);
err_free_bus: err_free_bus:
kfree(new_bus); kfree(new_bus);
err_free_priv:
kfree(priv);
return err; return err;
} }
...@@ -404,14 +418,16 @@ static int fsl_pq_mdio_remove(struct of_device *ofdev) ...@@ -404,14 +418,16 @@ static int fsl_pq_mdio_remove(struct of_device *ofdev)
{ {
struct device *device = &ofdev->dev; struct device *device = &ofdev->dev;
struct mii_bus *bus = dev_get_drvdata(device); struct mii_bus *bus = dev_get_drvdata(device);
struct fsl_pq_mdio_priv *priv = bus->priv;
mdiobus_unregister(bus); mdiobus_unregister(bus);
dev_set_drvdata(device, NULL); dev_set_drvdata(device, NULL);
iounmap(fsl_pq_mdio_get_regs(bus)); iounmap(priv->map);
bus->priv = NULL; bus->priv = NULL;
mdiobus_free(bus); mdiobus_free(bus);
kfree(priv);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment