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>
This commit is contained in:
Родитель
29fb00e047
Коммит
b3319b1052
|
@ -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,
|
||||||
|
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче