ath79: ag71xx: Split mdio driver into an independent platform device.
We need to have mdio1 belonging to gmac1 initialized before gmac0. Split it into a separated mdio device to get both mdios ready before probing gmac. Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
This commit is contained in:
parent
24cbe27bb7
commit
83d2dbc599
4 changed files with 78 additions and 84 deletions
|
@ -6,10 +6,10 @@ ag71xx-y += ag71xx_main.o
|
||||||
ag71xx-y += ag71xx_gmac.o
|
ag71xx-y += ag71xx_gmac.o
|
||||||
ag71xx-y += ag71xx_ethtool.o
|
ag71xx-y += ag71xx_ethtool.o
|
||||||
ag71xx-y += ag71xx_phy.o
|
ag71xx-y += ag71xx_phy.o
|
||||||
ag71xx-y += ag71xx_mdio.o
|
|
||||||
ag71xx-y += ag71xx_ar7240.o
|
ag71xx-y += ag71xx_ar7240.o
|
||||||
|
|
||||||
ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o
|
ag71xx-$(CONFIG_AG71XX_DEBUG_FS) += ag71xx_debugfs.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_AG71XX) += ag71xx_mdio.o
|
||||||
obj-$(CONFIG_AG71XX) += ag71xx.o
|
obj-$(CONFIG_AG71XX) += ag71xx.o
|
||||||
|
|
||||||
|
|
|
@ -170,12 +170,10 @@ struct ag71xx {
|
||||||
*/
|
*/
|
||||||
void __iomem *mac_base;
|
void __iomem *mac_base;
|
||||||
void __iomem *mii_base;
|
void __iomem *mii_base;
|
||||||
struct regmap *mii_regmap;
|
|
||||||
|
|
||||||
struct ag71xx_desc *stop_desc;
|
struct ag71xx_desc *stop_desc;
|
||||||
dma_addr_t stop_desc_dma;
|
dma_addr_t stop_desc_dma;
|
||||||
|
|
||||||
struct mii_bus *mii_bus;
|
|
||||||
struct phy_device *phy_dev;
|
struct phy_device *phy_dev;
|
||||||
void *phy_priv;
|
void *phy_priv;
|
||||||
int phy_if_mode;
|
int phy_if_mode;
|
||||||
|
@ -189,7 +187,6 @@ struct ag71xx {
|
||||||
|
|
||||||
struct reset_control *mac_reset;
|
struct reset_control *mac_reset;
|
||||||
struct reset_control *phy_reset;
|
struct reset_control *phy_reset;
|
||||||
struct reset_control *mdio_reset;
|
|
||||||
|
|
||||||
u32 fifodata[3];
|
u32 fifodata[3];
|
||||||
u32 plldata[3];
|
u32 plldata[3];
|
||||||
|
@ -201,6 +198,12 @@ struct ag71xx {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ag71xx_mdio {
|
||||||
|
struct reset_control *mdio_reset;
|
||||||
|
struct mii_bus *mii_bus;
|
||||||
|
struct regmap *mii_regmap;
|
||||||
|
};
|
||||||
|
|
||||||
extern struct ethtool_ops ag71xx_ethtool_ops;
|
extern struct ethtool_ops ag71xx_ethtool_ops;
|
||||||
void ag71xx_link_adjust(struct ag71xx *ag);
|
void ag71xx_link_adjust(struct ag71xx *ag);
|
||||||
|
|
||||||
|
@ -441,11 +444,6 @@ static inline void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag,
|
||||||
int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np);
|
int ag71xx_ar7240_init(struct ag71xx *ag, struct device_node *np);
|
||||||
void ag71xx_ar7240_cleanup(struct ag71xx *ag);
|
void ag71xx_ar7240_cleanup(struct ag71xx *ag);
|
||||||
|
|
||||||
int ag71xx_mdio_init(struct ag71xx *ag);
|
|
||||||
void ag71xx_mdio_cleanup(struct ag71xx *ag);
|
|
||||||
int ag71xx_mdio_mii_read(struct mii_bus *bus, int addr, int reg);
|
|
||||||
int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val);
|
|
||||||
|
|
||||||
int ag71xx_setup_gmac(struct device_node *np);
|
int ag71xx_setup_gmac(struct device_node *np);
|
||||||
|
|
||||||
int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg);
|
int ar7240sw_phy_read(struct mii_bus *mii, int addr, int reg);
|
||||||
|
|
|
@ -1438,11 +1438,10 @@ static int ag71xx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, 0);
|
ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, 0);
|
||||||
ag71xx_hw_init(ag);
|
ag71xx_hw_init(ag);
|
||||||
ag71xx_mdio_init(ag);
|
|
||||||
|
|
||||||
err = ag71xx_phy_connect(ag);
|
err = ag71xx_phy_connect(ag);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_mdio_free;
|
goto err_free;
|
||||||
|
|
||||||
err = ag71xx_debugfs_init(ag);
|
err = ag71xx_debugfs_init(ag);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -1466,8 +1465,6 @@ static int ag71xx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_phy_disconnect:
|
err_phy_disconnect:
|
||||||
ag71xx_phy_disconnect(ag);
|
ag71xx_phy_disconnect(ag);
|
||||||
err_mdio_free:
|
|
||||||
ag71xx_mdio_cleanup(ag);
|
|
||||||
err_free:
|
err_free:
|
||||||
free_netdev(dev);
|
free_netdev(dev);
|
||||||
return err;
|
return err;
|
||||||
|
@ -1484,7 +1481,6 @@ static int ag71xx_remove(struct platform_device *pdev)
|
||||||
ag = netdev_priv(dev);
|
ag = netdev_priv(dev);
|
||||||
ag71xx_debugfs_exit(ag);
|
ag71xx_debugfs_exit(ag);
|
||||||
ag71xx_phy_disconnect(ag);
|
ag71xx_phy_disconnect(ag);
|
||||||
ag71xx_mdio_cleanup(ag);
|
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
free_irq(dev->irq, dev);
|
free_irq(dev->irq, dev);
|
||||||
iounmap(ag->mac_base);
|
iounmap(ag->mac_base);
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
static int bus_count;
|
static int bus_count;
|
||||||
|
|
||||||
static int ag71xx_mdio_wait_busy(struct ag71xx *ag)
|
static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -29,57 +29,57 @@ static int ag71xx_mdio_wait_busy(struct ag71xx *ag)
|
||||||
|
|
||||||
udelay(AG71XX_MDIO_DELAY);
|
udelay(AG71XX_MDIO_DELAY);
|
||||||
|
|
||||||
regmap_read(ag->mii_regmap, AG71XX_REG_MII_IND, &busy);
|
regmap_read(am->mii_regmap, AG71XX_REG_MII_IND, &busy);
|
||||||
if (!busy)
|
if (!busy)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
udelay(AG71XX_MDIO_DELAY);
|
udelay(AG71XX_MDIO_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_err("%s: MDIO operation timed out\n", ag->mii_bus->name);
|
pr_err("%s: MDIO operation timed out\n", am->mii_bus->name);
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ag71xx_mdio_mii_read(struct mii_bus *bus, int addr, int reg)
|
static int ag71xx_mdio_mii_read(struct mii_bus *bus, int addr, int reg)
|
||||||
{
|
{
|
||||||
struct ag71xx *ag = bus->priv;
|
struct ag71xx_mdio *am = bus->priv;
|
||||||
int err;
|
int err;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
err = ag71xx_mdio_wait_busy(ag);
|
err = ag71xx_mdio_wait_busy(am);
|
||||||
if (err)
|
if (err)
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
|
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_ADDR,
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR,
|
||||||
((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
|
((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_READ);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_READ);
|
||||||
|
|
||||||
err = ag71xx_mdio_wait_busy(ag);
|
err = ag71xx_mdio_wait_busy(am);
|
||||||
if (err)
|
if (err)
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
|
|
||||||
regmap_read(ag->mii_regmap, AG71XX_REG_MII_STATUS, &ret);
|
regmap_read(am->mii_regmap, AG71XX_REG_MII_STATUS, &ret);
|
||||||
ret &= 0xffff;
|
ret &= 0xffff;
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
|
||||||
|
|
||||||
DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
|
DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
|
static int ag71xx_mdio_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
|
||||||
{
|
{
|
||||||
struct ag71xx *ag = bus->priv;
|
struct ag71xx_mdio *am = bus->priv;
|
||||||
|
|
||||||
DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
|
DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
|
||||||
|
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_ADDR,
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_ADDR,
|
||||||
((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
|
((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CTRL, val);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CTRL, val);
|
||||||
|
|
||||||
ag71xx_mdio_wait_busy(ag);
|
ag71xx_mdio_wait_busy(am);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -97,23 +97,22 @@ static int ar934x_mdio_clock_div(unsigned int rate)
|
||||||
static int ag71xx_mdio_reset(struct mii_bus *bus)
|
static int ag71xx_mdio_reset(struct mii_bus *bus)
|
||||||
{
|
{
|
||||||
struct device_node *np = bus->dev.of_node;
|
struct device_node *np = bus->dev.of_node;
|
||||||
struct ag71xx *ag = bus->priv;
|
struct ag71xx_mdio *am = bus->priv;
|
||||||
struct device_node *np_ag = ag->pdev->dev.of_node;
|
|
||||||
bool builtin_switch;
|
bool builtin_switch;
|
||||||
u32 t;
|
u32 t;
|
||||||
|
|
||||||
builtin_switch = of_property_read_bool(np, "builtin-switch");
|
builtin_switch = of_property_read_bool(np, "builtin-switch");
|
||||||
|
|
||||||
if (of_device_is_compatible(np_ag, "qca,ar7240-eth"))
|
if (of_device_is_compatible(np, "qca,ar7240-mdio"))
|
||||||
t = MII_CFG_CLK_DIV_6;
|
t = MII_CFG_CLK_DIV_6;
|
||||||
else if (of_device_is_compatible(np_ag, "qca,ar9340-eth"))
|
else if (of_device_is_compatible(np, "qca,ar9340-mdio"))
|
||||||
t = MII_CFG_CLK_DIV_58;
|
t = MII_CFG_CLK_DIV_58;
|
||||||
else if (builtin_switch)
|
else if (builtin_switch)
|
||||||
t = MII_CFG_CLK_DIV_10;
|
t = MII_CFG_CLK_DIV_10;
|
||||||
else
|
else
|
||||||
t = MII_CFG_CLK_DIV_28;
|
t = MII_CFG_CLK_DIV_28;
|
||||||
|
|
||||||
if (builtin_switch && of_device_is_compatible(np_ag, "qca,ar9340-eth")) {
|
if (builtin_switch && of_device_is_compatible(np, "qca,ar9340-mdio")) {
|
||||||
struct clk *ref_clk = of_clk_get(np, 0);
|
struct clk *ref_clk = of_clk_get(np, 0);
|
||||||
int clock_rate;
|
int clock_rate;
|
||||||
|
|
||||||
|
@ -126,56 +125,45 @@ static int ag71xx_mdio_reset(struct mii_bus *bus)
|
||||||
clk_put(ref_clk);
|
clk_put(ref_clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CFG, t | MII_CFG_RESET);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t | MII_CFG_RESET);
|
||||||
udelay(100);
|
udelay(100);
|
||||||
|
|
||||||
regmap_write(ag->mii_regmap, AG71XX_REG_MII_CFG, t);
|
regmap_write(am->mii_regmap, AG71XX_REG_MII_CFG, t);
|
||||||
udelay(100);
|
udelay(100);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ag71xx_mdio_init(struct ag71xx *ag)
|
static int ag71xx_mdio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *parent = &ag->pdev->dev;
|
struct device *amdev = &pdev->dev;
|
||||||
struct device_node *np;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct ag71xx_mdio *am;
|
||||||
struct mii_bus *mii_bus;
|
struct mii_bus *mii_bus;
|
||||||
bool builtin_switch;
|
bool builtin_switch;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
np = of_get_child_by_name(parent->of_node, "mdio-bus");
|
am = devm_kzalloc(amdev, sizeof(*am), GFP_KERNEL);
|
||||||
if (!np)
|
if (!am)
|
||||||
return -ENODEV;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (!of_device_is_available(np)) {
|
am->mii_regmap = syscon_regmap_lookup_by_phandle(np, "regmap");
|
||||||
err = 0;
|
if (!am->mii_regmap)
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ag->mii_regmap = syscon_regmap_lookup_by_phandle(np, "regmap");
|
|
||||||
if (!ag->mii_regmap)
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
mii_bus = devm_mdiobus_alloc(parent);
|
mii_bus = devm_mdiobus_alloc(amdev);
|
||||||
if (!mii_bus) {
|
if (!mii_bus)
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ag->mdio_reset = of_reset_control_get_exclusive(np, "mdio");
|
am->mdio_reset = of_reset_control_get_exclusive(np, "mdio");
|
||||||
builtin_switch = of_property_read_bool(np, "builtin-switch");
|
builtin_switch = of_property_read_bool(np, "builtin-switch");
|
||||||
|
|
||||||
mii_bus->name = "mdio";
|
mii_bus->name = "ag71xx_mdio";
|
||||||
if (builtin_switch) {
|
mii_bus->read = ag71xx_mdio_mii_read;
|
||||||
mii_bus->read = ar7240sw_phy_read;
|
mii_bus->write = ag71xx_mdio_mii_write;
|
||||||
mii_bus->write = ar7240sw_phy_write;
|
|
||||||
} else {
|
|
||||||
mii_bus->read = ag71xx_mdio_mii_read;
|
|
||||||
mii_bus->write = ag71xx_mdio_mii_write;
|
|
||||||
}
|
|
||||||
mii_bus->reset = ag71xx_mdio_reset;
|
mii_bus->reset = ag71xx_mdio_reset;
|
||||||
mii_bus->priv = ag;
|
mii_bus->priv = am;
|
||||||
mii_bus->parent = parent;
|
mii_bus->parent = amdev;
|
||||||
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s.%d", np->name, bus_count++);
|
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s.%d", np->name, bus_count++);
|
||||||
|
|
||||||
if (!builtin_switch &&
|
if (!builtin_switch &&
|
||||||
|
@ -185,34 +173,46 @@ int ag71xx_mdio_init(struct ag71xx *ag)
|
||||||
for (i = 0; i < PHY_MAX_ADDR; i++)
|
for (i = 0; i < PHY_MAX_ADDR; i++)
|
||||||
mii_bus->irq[i] = PHY_POLL;
|
mii_bus->irq[i] = PHY_POLL;
|
||||||
|
|
||||||
if (!IS_ERR(ag->mdio_reset)) {
|
if (!IS_ERR(am->mdio_reset)) {
|
||||||
reset_control_assert(ag->mdio_reset);
|
reset_control_assert(am->mdio_reset);
|
||||||
msleep(100);
|
msleep(100);
|
||||||
reset_control_deassert(ag->mdio_reset);
|
reset_control_deassert(am->mdio_reset);
|
||||||
msleep(200);
|
msleep(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = of_mdiobus_register(mii_bus, np);
|
err = of_mdiobus_register(mii_bus, np);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_out;
|
return err;
|
||||||
|
|
||||||
ag->mii_bus = mii_bus;
|
am->mii_bus = mii_bus;
|
||||||
|
platform_set_drvdata(pdev, am);
|
||||||
if (builtin_switch)
|
|
||||||
ag71xx_ar7240_init(ag, np);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_out:
|
|
||||||
of_node_put(np);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ag71xx_mdio_cleanup(struct ag71xx *ag)
|
static int ag71xx_mdio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
if (!ag->mii_bus)
|
struct ag71xx_mdio *am = platform_get_drvdata(pdev);
|
||||||
return;
|
|
||||||
|
|
||||||
ag71xx_ar7240_cleanup(ag);
|
mdiobus_unregister(am->mii_bus);
|
||||||
mdiobus_unregister(ag->mii_bus);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id ag71xx_mdio_match[] = {
|
||||||
|
{ .compatible = "qca,ar7240-mdio" },
|
||||||
|
{ .compatible = "qca,ar9340-mdio" },
|
||||||
|
{ .compatible = "qca,ath79-mdio" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver ag71xx_mdio_driver = {
|
||||||
|
.probe = ag71xx_mdio_probe,
|
||||||
|
.remove = ag71xx_mdio_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "ag71xx-mdio",
|
||||||
|
.of_match_table = ag71xx_mdio_match,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(ag71xx_mdio_driver);
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
Loading…
Reference in a new issue