Commit 95aafe91 authored by Linus Walleij's avatar Linus Walleij Committed by David S. Miller

net: ethernet: ixp4xx: Support device tree probing

This adds device tree probing to the IXP4xx ethernet
driver.

Add a platform data bool to tell us whether to
register an MDIO bus for the device or not, as well
as the corresponding NPE.

We need to drop the memory region request as part of
this since the OF core will request the memory for the
device.

Cc: Zoltan HERPAI <wigyori@uid0.hu>
Cc: Raylynn Knight <rayknight@me.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3e8047a9
......@@ -22,6 +22,7 @@ config IXP4XX_ETH
tristate "Intel IXP4xx Ethernet support"
depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
select PHYLIB
select OF_MDIO if OF
select NET_PTP_CLASSIFY
help
Say Y here if you want to use built-in Ethernet ports
......
......@@ -28,6 +28,7 @@
#include <linux/kernel.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/platform_data/eth_ixp4xx.h>
#include <linux/platform_device.h>
......@@ -165,7 +166,6 @@ struct eth_regs {
};
struct port {
struct resource *mem_res;
struct eth_regs __iomem *regs;
struct npe *npe;
struct net_device *netdev;
......@@ -250,6 +250,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
static DEFINE_SPINLOCK(mdio_lock);
static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
static struct mii_bus *mdio_bus;
static struct device_node *mdio_bus_np;
static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
......@@ -533,7 +534,8 @@ static int ixp4xx_mdio_register(struct eth_regs __iomem *regs)
mdio_bus->write = &ixp4xx_mdio_write;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "ixp4xx-eth-0");
if ((err = mdiobus_register(mdio_bus)))
err = of_mdiobus_register(mdio_bus, mdio_bus_np);
if (err)
mdiobus_free(mdio_bus);
return err;
}
......@@ -1358,78 +1360,148 @@ static const struct net_device_ops ixp4xx_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
};
#ifdef CONFIG_OF
static struct eth_plat_info *ixp4xx_of_get_platdata(struct device *dev)
{
struct device_node *np = dev->of_node;
struct of_phandle_args queue_spec;
struct of_phandle_args npe_spec;
struct device_node *mdio_np;
struct eth_plat_info *plat;
int ret;
plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
return NULL;
ret = of_parse_phandle_with_fixed_args(np, "intel,npe-handle", 1, 0,
&npe_spec);
if (ret) {
dev_err(dev, "no NPE engine specified\n");
return NULL;
}
/* NPE ID 0x00, 0x10, 0x20... */
plat->npe = (npe_spec.args[0] << 4);
/* Check if this device has an MDIO bus */
mdio_np = of_get_child_by_name(np, "mdio");
if (mdio_np) {
plat->has_mdio = true;
mdio_bus_np = mdio_np;
/* DO NOT put the mdio_np, it will be used */
}
/* Get the rx queue as a resource from queue manager */
ret = of_parse_phandle_with_fixed_args(np, "queue-rx", 1, 0,
&queue_spec);
if (ret) {
dev_err(dev, "no rx queue phandle\n");
return NULL;
}
plat->rxq = queue_spec.args[0];
/* Get the txready queue as resource from queue manager */
ret = of_parse_phandle_with_fixed_args(np, "queue-txready", 1, 0,
&queue_spec);
if (ret) {
dev_err(dev, "no txready queue phandle\n");
return NULL;
}
plat->txreadyq = queue_spec.args[0];
return plat;
}
#else
static struct eth_plat_info *ixp4xx_of_get_platdata(struct device *dev)
{
return NULL;
}
#endif
static int ixp4xx_eth_probe(struct platform_device *pdev)
{
struct phy_device *phydev = NULL;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct eth_plat_info *plat;
resource_size_t regs_phys;
struct net_device *ndev;
struct resource *res;
struct port *port;
int err;
if (np) {
plat = ixp4xx_of_get_platdata(dev);
if (!plat)
return -ENODEV;
} else {
plat = dev_get_platdata(dev);
if (!(ndev = devm_alloc_etherdev(dev, sizeof(struct port))))
return -ENOMEM;
SET_NETDEV_DEV(ndev, dev);
port = netdev_priv(ndev);
port->netdev = ndev;
port->id = pdev->id;
/* Get the port resource and remap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
if (!plat)
return -ENODEV;
regs_phys = res->start;
port->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(port->regs))
return PTR_ERR(port->regs);
switch (port->id) {
plat->npe = pdev->id;
switch (plat->npe) {
case IXP4XX_ETH_NPEA:
/* If the MDIO bus is not up yet, defer probe */
if (!mdio_bus)
return -EPROBE_DEFER;
break;
case IXP4XX_ETH_NPEB:
/*
* On all except IXP43x, NPE-B is used for the MDIO bus.
* If there is no NPE-B in the feature set, bail out, else
* register the MDIO bus.
/* On all except IXP43x, NPE-B is used for the MDIO bus.
* If there is no NPE-B in the feature set, bail out,
* else we have the MDIO bus here.
*/
if (!cpu_is_ixp43x()) {
if (!(ixp4xx_read_feature_bits() &
IXP4XX_FEATURE_NPEB_ETH0))
return -ENODEV;
/* Else register the MDIO bus on NPE-B */
if ((err = ixp4xx_mdio_register(port->regs)))
return err;
plat->has_mdio = true;
}
if (!mdio_bus)
return -EPROBE_DEFER;
break;
case IXP4XX_ETH_NPEC:
/*
* IXP43x lacks NPE-B and uses NPE-C for the MDIO bus access,
* of there is no NPE-C, no bus, nothing works, so bail out.
/* IXP43x lacks NPE-B and uses NPE-C for the MDIO bus
* access, if there is no NPE-C, no bus, nothing works,
* so bail out.
*/
if (cpu_is_ixp43x()) {
if (!(ixp4xx_read_feature_bits() &
IXP4XX_FEATURE_NPEC_ETH))
return -ENODEV;
/* Else register the MDIO bus on NPE-C */
if ((err = ixp4xx_mdio_register(port->regs)))
return err;
/* Else register the MDIO bus on NPE-B */
plat->has_mdio = true;
}
if (!mdio_bus)
return -EPROBE_DEFER;
break;
default:
return -ENODEV;
}
}
if (!(ndev = devm_alloc_etherdev(dev, sizeof(struct port))))
return -ENOMEM;
SET_NETDEV_DEV(ndev, dev);
port = netdev_priv(ndev);
port->netdev = ndev;
port->id = plat->npe;
/* Get the port resource and remap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
port->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(port->regs))
return PTR_ERR(port->regs);
/* Register the MDIO bus if we have it */
if (plat->has_mdio) {
err = ixp4xx_mdio_register(port->regs);
if (err) {
dev_err(dev, "failed to register MDIO bus\n");
return err;
}
}
/* If the instance with the MDIO bus has not yet appeared,
* defer probing until it gets probed.
*/
if (!mdio_bus)
return -EPROBE_DEFER;
ndev->netdev_ops = &ixp4xx_netdev_ops;
ndev->ethtool_ops = &ixp4xx_ethtool_ops;
......@@ -1440,12 +1512,6 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
if (!(port->npe = npe_request(NPE_ID(port->id))))
return -EIO;
port->mem_res = request_mem_region(regs_phys, REGS_SIZE, ndev->name);
if (!port->mem_res) {
err = -EBUSY;
goto err_npe_rel;
}
port->plat = plat;
npe_port_tab[NPE_ID(port->id)] = port;
memcpy(ndev->dev_addr, plat->hwaddr, ETH_ALEN);
......@@ -1458,9 +1524,13 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
__raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control);
udelay(50);
if (np) {
phydev = of_phy_get_and_connect(ndev, np, ixp4xx_adjust_link);
} else {
phydev = mdiobus_get_phy(mdio_bus, plat->phy);
if (IS_ERR(phydev)) {
err = PTR_ERR(phydev);
dev_err(dev, "could not connect phydev (%d)\n", err);
goto err_free_mem;
}
err = phy_connect_direct(ndev, phydev, ixp4xx_adjust_link,
......@@ -1468,6 +1538,13 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
if (err)
goto err_free_mem;
}
if (!phydev) {
err = -ENODEV;
dev_err(dev, "no phydev\n");
goto err_free_mem;
}
phydev->irq = PHY_POLL;
if ((err = register_netdev(ndev)))
......@@ -1482,8 +1559,6 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
phy_disconnect(phydev);
err_free_mem:
npe_port_tab[NPE_ID(port->id)] = NULL;
release_resource(port->mem_res);
err_npe_rel:
npe_release(port->npe);
return err;
}
......@@ -1499,12 +1574,21 @@ static int ixp4xx_eth_remove(struct platform_device *pdev)
ixp4xx_mdio_remove();
npe_port_tab[NPE_ID(port->id)] = NULL;
npe_release(port->npe);
release_resource(port->mem_res);
return 0;
}
static const struct of_device_id ixp4xx_eth_of_match[] = {
{
.compatible = "intel,ixp4xx-ethernet",
},
{ },
};
static struct platform_driver ixp4xx_eth_driver = {
.driver.name = DRV_NAME,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(ixp4xx_eth_of_match),
},
.probe = ixp4xx_eth_probe,
.remove = ixp4xx_eth_remove,
};
......
......@@ -14,6 +14,8 @@ struct eth_plat_info {
u8 rxq; /* configurable, currently 0 - 31 only */
u8 txreadyq;
u8 hwaddr[6];
u8 npe; /* NPE instance used by this interface */
bool has_mdio; /* If this instance has an MDIO bus */
};
#endif
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