summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/gianfar.c321
-rw-r--r--drivers/net/gianfar.h21
-rw-r--r--drivers/net/gianfar_ethtool.c22
-rw-r--r--drivers/net/gianfar_mii.c212
-rw-r--r--drivers/net/gianfar_mii.h2
5 files changed, 386 insertions, 192 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 55e319fa7fe6..7398704c4b55 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -25,11 +25,8 @@
*
* Theory of operation
*
- * The driver is initialized through platform_device. Structures which
- * define the configuration needed by the board are defined in a
- * board structure in arch/ppc/platforms (though I do not
- * discount the possibility that other architectures could one
- * day be supported.
+ * The driver is initialized through of_device. Configuration information
+ * is therefore conveyed through an OF-style device tree.
*
* The Gianfar Ethernet Controller uses a ring of buffer
* descriptors. The beginning is indicated by a register
@@ -78,7 +75,7 @@
#include <linux/if_vlan.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -92,6 +89,8 @@
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <linux/of.h>
#include "gianfar.h"
#include "gianfar_mii.h"
@@ -119,8 +118,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id);
static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
-static int gfar_probe(struct platform_device *pdev);
-static int gfar_remove(struct platform_device *pdev);
+static int gfar_probe(struct of_device *ofdev,
+ const struct of_device_id *match);
+static int gfar_remove(struct of_device *ofdev);
static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
@@ -152,25 +152,158 @@ static inline int gfar_uses_fcb(struct gfar_private *priv)
return (priv->vlan_enable || priv->rx_csum_enable);
}
+static int gfar_of_init(struct net_device *dev)
+{
+ struct device_node *phy, *mdio;
+ const unsigned int *id;
+ const char *model;
+ const char *ctype;
+ const void *mac_addr;
+ const phandle *ph;
+ u64 addr, size;
+ int err = 0;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct device_node *np = priv->node;
+ char bus_name[MII_BUS_ID_SIZE];
+
+ if (!np || !of_device_is_available(np))
+ return -ENODEV;
+
+ /* get a pointer to the register memory */
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ priv->regs = ioremap(addr, size);
+
+ if (priv->regs == NULL)
+ return -ENOMEM;
+
+ priv->interruptTransmit = irq_of_parse_and_map(np, 0);
+
+ model = of_get_property(np, "model", NULL);
+
+ /* If we aren't the FEC we have multiple interrupts */
+ if (model && strcasecmp(model, "FEC")) {
+ priv->interruptReceive = irq_of_parse_and_map(np, 1);
+
+ priv->interruptError = irq_of_parse_and_map(np, 2);
+
+ if (priv->interruptTransmit < 0 ||
+ priv->interruptReceive < 0 ||
+ priv->interruptError < 0) {
+ err = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ mac_addr = of_get_mac_address(np);
+ if (mac_addr)
+ memcpy(dev->dev_addr, mac_addr, MAC_ADDR_LEN);
+
+ if (model && !strcasecmp(model, "TSEC"))
+ priv->device_flags =
+ FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE |
+ FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR;
+ if (model && !strcasecmp(model, "eTSEC"))
+ priv->device_flags =
+ FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE |
+ FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR |
+ FSL_GIANFAR_DEV_HAS_CSUM |
+ FSL_GIANFAR_DEV_HAS_VLAN |
+ FSL_GIANFAR_DEV_HAS_MAGIC_PACKET |
+ FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
+
+ ctype = of_get_property(np, "phy-connection-type", NULL);
+
+ /* We only care about rgmii-id. The rest are autodetected */
+ if (ctype && !strcmp(ctype, "rgmii-id"))
+ priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
+ else
+ priv->interface = PHY_INTERFACE_MODE_MII;
+
+ if (of_get_property(np, "fsl,magic-packet", NULL))
+ priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
+
+ ph = of_get_property(np, "phy-handle", NULL);
+ if (ph == NULL) {
+ u32 *fixed_link;
+
+ fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL);
+ if (!fixed_link) {
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ snprintf(priv->phy_bus_id, BUS_ID_SIZE, PHY_ID_FMT, "0",
+ fixed_link[0]);
+ } else {
+ phy = of_find_node_by_phandle(*ph);
+
+ if (phy == NULL) {
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ mdio = of_get_parent(phy);
+
+ id = of_get_property(phy, "reg", NULL);
+
+ of_node_put(phy);
+ of_node_put(mdio);
+
+ gfar_mdio_bus_name(bus_name, mdio);
+ snprintf(priv->phy_bus_id, BUS_ID_SIZE, "%s:%02x",
+ bus_name, *id);
+ }
+
+ /* Find the TBI PHY. If it's not there, we don't support SGMII */
+ ph = of_get_property(np, "tbi-handle", NULL);
+ if (ph) {
+ struct device_node *tbi = of_find_node_by_phandle(*ph);
+ struct of_device *ofdev;
+ struct mii_bus *bus;
+
+ if (!tbi)
+ return 0;
+
+ mdio = of_get_parent(tbi);
+ if (!mdio)
+ return 0;
+
+ ofdev = of_find_device_by_node(mdio);
+
+ of_node_put(mdio);
+
+ id = of_get_property(tbi, "reg", NULL);
+ if (!id)
+ return 0;
+
+ of_node_put(tbi);
+
+ bus = dev_get_drvdata(&ofdev->dev);
+
+ priv->tbiphy = bus->phy_map[*id];
+ }
+
+ return 0;
+
+err_out:
+ iounmap(priv->regs);
+ return err;
+}
+
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
-static int gfar_probe(struct platform_device *pdev)
+static int gfar_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
{
u32 tempval;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
- struct gianfar_platform_data *einfo;
- struct resource *r;
- int err = 0, irq;
-
- einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
-
- if (NULL == einfo) {
- printk(KERN_ERR "gfar %d: Missing additional data!\n",
- pdev->id);
-
- return -ENODEV;
- }
+ int err = 0;
+ DECLARE_MAC_BUF(mac);
/* Create an ethernet device instance */
dev = alloc_etherdev(sizeof (*priv));
@@ -180,48 +313,19 @@ static int gfar_probe(struct platform_device *pdev)
priv = netdev_priv(dev);
priv->dev = dev;
+ priv->node = ofdev->node;
- /* Set the info in the priv to the current info */
- priv->einfo = einfo;
-
- /* fill out IRQ fields */
- if (einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- irq = platform_get_irq_byname(pdev, "tx");
- if (irq < 0)
- goto regs_fail;
- priv->interruptTransmit = irq;
-
- irq = platform_get_irq_byname(pdev, "rx");
- if (irq < 0)
- goto regs_fail;
- priv->interruptReceive = irq;
-
- irq = platform_get_irq_byname(pdev, "error");
- if (irq < 0)
- goto regs_fail;
- priv->interruptError = irq;
- } else {
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- goto regs_fail;
- priv->interruptTransmit = irq;
- }
-
- /* get a pointer to the register memory */
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = ioremap(r->start, sizeof (struct gfar));
+ err = gfar_of_init(dev);
- if (NULL == priv->regs) {
- err = -ENOMEM;
+ if (err)
goto regs_fail;
- }
spin_lock_init(&priv->txlock);
spin_lock_init(&priv->rxlock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
- platform_set_drvdata(pdev, dev);
+ dev_set_drvdata(&ofdev->dev, priv);
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
@@ -239,13 +343,10 @@ static int gfar_probe(struct platform_device *pdev)
/* Initialize ECNTRL */
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
- /* Copy the station address into the dev structure, */
- memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
-
/* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs);
- SET_NETDEV_DEV(dev, &pdev->dev);
+ SET_NETDEV_DEV(dev, &ofdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -263,7 +364,7 @@ static int gfar_probe(struct platform_device *pdev)
dev->ethtool_ops = &gfar_ethtool_ops;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
dev->features |= NETIF_F_IP_CSUM;
} else
@@ -271,7 +372,7 @@ static int gfar_probe(struct platform_device *pdev)
priv->vlgrp = NULL;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
dev->vlan_rx_register = gfar_vlan_rx_register;
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
@@ -279,7 +380,7 @@ static int gfar_probe(struct platform_device *pdev)
priv->vlan_enable = 1;
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
priv->extended_hash = 1;
priv->hash_width = 9;
@@ -314,7 +415,7 @@ static int gfar_probe(struct platform_device *pdev)
priv->hash_regs[7] = &priv->regs->gaddr7;
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
priv->padding = DEFAULT_PADDING;
else
priv->padding = 0;
@@ -368,29 +469,28 @@ regs_fail:
return err;
}
-static int gfar_remove(struct platform_device *pdev)
+static int gfar_remove(struct of_device *ofdev)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
- platform_set_drvdata(pdev, NULL);
+ dev_set_drvdata(&ofdev->dev, NULL);
iounmap(priv->regs);
- free_netdev(dev);
+ free_netdev(priv->dev);
return 0;
}
#ifdef CONFIG_PM
-static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
+static int gfar_suspend(struct of_device *ofdev, pm_message_t state)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
+ struct net_device *dev = priv->dev;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
- (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(dev);
@@ -431,14 +531,14 @@ static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int gfar_resume(struct platform_device *pdev)
+static int gfar_resume(struct of_device *ofdev)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
+ struct net_device *dev = priv->dev;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
- (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
if (!netif_running(dev)) {
netif_device_attach(dev);
@@ -497,7 +597,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
if (ecntrl & ECNTRL_REDUCED_MII_MODE)
return PHY_INTERFACE_MODE_RMII;
else {
- phy_interface_t interface = priv->einfo->interface;
+ phy_interface_t interface = priv->interface;
/*
* This isn't autodetected right now, so it must
@@ -510,7 +610,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
}
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
return PHY_INTERFACE_MODE_GMII;
return PHY_INTERFACE_MODE_MII;
@@ -524,21 +624,18 @@ static int init_phy(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
- priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
+ priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
SUPPORTED_1000baseT_Full : 0;
struct phy_device *phydev;
- char phy_id[BUS_ID_SIZE];
phy_interface_t interface;
priv->oldlink = 0;
priv->oldspeed = 0;
priv->oldduplex = -1;
- snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, priv->einfo->bus_id, priv->einfo->phy_id);
-
interface = gfar_get_interface(dev);
- phydev = phy_connect(dev, phy_id, &adjust_link, 0, interface);
+ phydev = phy_connect(dev, priv->phy_bus_id, &adjust_link, 0, interface);
if (interface == PHY_INTERFACE_MODE_SGMII)
gfar_configure_serdes(dev);
@@ -569,35 +666,31 @@ static int init_phy(struct net_device *dev)
static void gfar_configure_serdes(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_mii __iomem *regs =
- (void __iomem *)&priv->regs->gfar_mii_regs;
- int tbipa = gfar_read(&priv->regs->tbipa);
- struct mii_bus *bus = gfar_get_miibus(priv);
- if (bus)
- mutex_lock(&bus->mdio_lock);
+ if (!priv->tbiphy) {
+ printk(KERN_WARNING "SGMII mode requires that the device "
+ "tree specify a tbi-handle\n");
+ return;
+ }
- /* If the link is already up, we must already be ok, and don't need to
+ /*
+ * If the link is already up, we must already be ok, and don't need to
* configure and reset the TBI<->SerDes link. Maybe U-Boot configured
* everything for us? Resetting it takes the link down and requires
* several seconds for it to come back.
*/
- if (gfar_local_mdio_read(regs, tbipa, MII_BMSR) & BMSR_LSTATUS)
- goto done;
+ if (phy_read(priv->tbiphy, MII_BMSR) & BMSR_LSTATUS)
+ return;
/* Single clk mode, mii mode off(for serdes communication) */
- gfar_local_mdio_write(regs, tbipa, MII_TBICON, TBICON_CLK_SELECT);
+ phy_write(priv->tbiphy, MII_TBICON, TBICON_CLK_SELECT);
- gfar_local_mdio_write(regs, tbipa, MII_ADVERTISE,
+ phy_write(priv->tbiphy, MII_ADVERTISE,
ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
ADVERTISE_1000XPSE_ASYM);
- gfar_local_mdio_write(regs, tbipa, MII_BMCR, BMCR_ANENABLE |
+ phy_write(priv->tbiphy, MII_BMCR, BMCR_ANENABLE |
BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000);
-
- done:
- if (bus)
- mutex_unlock(&bus->mdio_lock);
}
static void init_registers(struct net_device *dev)
@@ -630,7 +723,7 @@ static void init_registers(struct net_device *dev)
gfar_write(&priv->regs->gaddr7, 0);
/* Zero out the rmon mib registers if it has them */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib));
/* Mask off the CAM interrupts */
@@ -705,7 +798,7 @@ void stop_gfar(struct net_device *dev)
spin_unlock_irqrestore(&priv->txlock, flags);
/* Free the IRQs */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
free_irq(priv->interruptError, dev);
free_irq(priv->interruptTransmit, dev);
free_irq(priv->interruptReceive, dev);
@@ -919,7 +1012,7 @@ int startup_gfar(struct net_device *dev)
/* If the device has multiple interrupts, register for
* them. Otherwise, only register for the one */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
if (request_irq(priv->interruptError, gfar_error,
@@ -1751,7 +1844,7 @@ static void gfar_netpoll(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
/* If the device has multiple interrupts, run tx/rx */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
disable_irq(priv->interruptTransmit);
disable_irq(priv->interruptReceive);
disable_irq(priv->interruptError);
@@ -2045,7 +2138,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);
/* Magic Packet is not an error. */
- if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+ if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
(events & IEVENT_MAG))
events &= ~IEVENT_MAG;
@@ -2111,16 +2204,24 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:fsl-gianfar");
+static struct of_device_id gfar_match[] =
+{
+ {
+ .type = "network",
+ .compatible = "gianfar",
+ },
+ {},
+};
+
/* Structure for a device driver */
-static struct platform_driver gfar_driver = {
+static struct of_platform_driver gfar_driver = {
+ .name = "fsl-gianfar",
+ .match_table = gfar_match,
+
.probe = gfar_probe,
.remove = gfar_remove,
.suspend = gfar_suspend,
.resume = gfar_resume,
- .driver = {
- .name = "fsl-gianfar",
- .owner = THIS_MODULE,
- },
};
static int __init gfar_init(void)
@@ -2130,7 +2231,7 @@ static int __init gfar_init(void)
if (err)
return err;
- err = platform_driver_register(&gfar_driver);
+ err = of_register_platform_driver(&gfar_driver);
if (err)
gfar_mdio_exit();
@@ -2140,7 +2241,7 @@ static int __init gfar_init(void)
static void __exit gfar_exit(void)
{
- platform_driver_unregister(&gfar_driver);
+ of_unregister_platform_driver(&gfar_driver);
gfar_mdio_exit();
}
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index f46e9b63af13..ca7f0a6a68c5 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -657,6 +657,19 @@ struct gfar {
};
+/* Flags related to gianfar device features */
+#define FSL_GIANFAR_DEV_HAS_GIGABIT 0x00000001
+#define FSL_GIANFAR_DEV_HAS_COALESCE 0x00000002
+#define FSL_GIANFAR_DEV_HAS_RMON 0x00000004
+#define FSL_GIANFAR_DEV_HAS_MULTI_INTR 0x00000008
+#define FSL_GIANFAR_DEV_HAS_CSUM 0x00000010
+#define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020
+#define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040
+#define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080
+#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100
+#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
+#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblence)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -694,6 +707,7 @@ struct gfar_private {
/* RX Locked fields */
spinlock_t rxlock;
+ struct device_node *node;
struct net_device *dev;
struct napi_struct napi;
@@ -733,6 +747,9 @@ struct gfar_private {
/* Bitfield update lock */
spinlock_t bflock;
+ phy_interface_t interface;
+ char phy_bus_id[BUS_ID_SIZE];
+ u32 device_flags;
unsigned char vlan_enable:1,
rx_csum_enable:1,
extended_hash:1,
@@ -744,11 +761,9 @@ struct gfar_private {
unsigned int interruptReceive;
unsigned int interruptError;
- /* info structure initialized by platform code */
- struct gianfar_platform_data *einfo;
-
/* PHY stuff */
struct phy_device *phydev;
+ struct phy_device *tbiphy;
struct mii_bus *mii_bus;
int oldspeed;
int oldduplex;
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index fb7d3ccc0fdc..53944b120a3d 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -121,7 +121,7 @@ static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
{
struct gfar_private *priv = netdev_priv(dev);
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
else
memcpy(buf, stat_gstrings,
@@ -138,7 +138,7 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -158,7 +158,7 @@ static int gfar_sset_count(struct net_device *dev, int sset)
switch (sset) {
case ETH_SS_STATS:
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
return GFAR_STATS_LEN;
else
return GFAR_EXTRA_STATS_LEN;
@@ -280,7 +280,7 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
if (NULL == priv->phydev)
@@ -332,7 +332,7 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
/* Set up rx coalescing */
@@ -482,7 +482,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
unsigned long flags;
int err = 0;
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
if (dev->flags & IFF_UP) {
@@ -515,7 +515,7 @@ static uint32_t gfar_get_rx_csum(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return 0;
return priv->rx_csum_enable;
@@ -526,7 +526,7 @@ static int gfar_set_tx_csum(struct net_device *dev, uint32_t data)
unsigned long flags;
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
spin_lock_irqsave(&priv->txlock, flags);
@@ -547,7 +547,7 @@ static uint32_t gfar_get_tx_csum(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return 0;
return (dev->features & NETIF_F_IP_CSUM) != 0;
@@ -570,7 +570,7 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct gfar_private *priv = netdev_priv(dev);
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
wol->supported = WAKE_MAGIC;
wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
} else {
@@ -583,7 +583,7 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
wol->wolopts != 0)
return -EINVAL;
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c
index 0e2595d24933..f3706e415b45 100644
--- a/drivers/net/gianfar_mii.c
+++ b/drivers/net/gianfar_mii.c
@@ -34,6 +34,8 @@
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -150,19 +152,83 @@ static int gfar_mdio_reset(struct mii_bus *bus)
return 0;
}
+/* Allocate an array which provides irq #s for each PHY on the given bus */
+static int *create_irq_map(struct device_node *np)
+{
+ int *irqs;
+ int i;
+ struct device_node *child = NULL;
+
+ irqs = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
+
+ if (!irqs)
+ return NULL;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ irqs[i] = PHY_POLL;
+
+ while ((child = of_get_next_child(np, child)) != NULL) {
+ int irq = irq_of_parse_and_map(child, 0);
+ const u32 *id;
+
+ if (irq == NO_IRQ)
+ continue;
+
+ id = of_get_property(child, "reg", NULL);
+
+ if (!id)
+ continue;
+
+ if (*id < PHY_MAX_ADDR && *id >= 0)
+ irqs[*id] = irq;
+ else
+ printk(KERN_WARNING "%s: "
+ "%d is not a valid PHY address\n",
+ np->full_name, *id);
+ }
+
+ return irqs;
+}
+
+
+void gfar_mdio_bus_name(char *name, struct device_node *np)
+{
+ const u32 *reg;
+
+ reg = of_get_property(np, "reg", NULL);
-static int gfar_mdio_probe(struct device *dev)
+ snprintf(name, MII_BUS_ID_SIZE, "%s@%x", np->name, reg ? *reg : 0);
+}
+
+/* Scan the bus in reverse, looking for an empty spot */
+static int gfar_mdio_find_free(struct mii_bus *new_bus)
+{
+ int i;
+
+ for (i = PHY_MAX_ADDR; i > 0; i--) {
+ u32 phy_id;
+
+ if (get_phy_id(new_bus, i, &phy_id))
+ return -1;
+
+ if (phy_id == 0xffffffff)
+ break;
+ }
+
+ return i;
+}
+
+static int gfar_mdio_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gianfar_mdio_data *pdata;
struct gfar_mii __iomem *regs;
struct gfar __iomem *enet_regs;
struct mii_bus *new_bus;
- struct resource *r;
- int i, err = 0;
-
- if (NULL == dev)
- return -EINVAL;
+ int err = 0;
+ u64 addr, size;
+ struct device_node *np = ofdev->node;
+ struct device_node *tbi;
+ int tbiaddr = -1;
new_bus = mdiobus_alloc();
if (NULL == new_bus)
@@ -172,31 +238,28 @@ static int gfar_mdio_probe(struct device *dev)
new_bus->read = &gfar_mdio_read,
new_bus->write = &gfar_mdio_write,
new_bus->reset = &gfar_mdio_reset,
- snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
-
- pdata = (struct gianfar_mdio_data *)pdev->dev.platform_data;
-
- if (NULL == pdata) {
- printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
- return -ENODEV;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gfar_mdio_bus_name(new_bus->id, np);
/* Set the PHY base address */
- regs = ioremap(r->start, sizeof (struct gfar_mii));
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ regs = ioremap(addr, size);
if (NULL == regs) {
err = -ENOMEM;
- goto reg_map_fail;
+ goto err_free_bus;
}
new_bus->priv = (void __force *)regs;
- new_bus->irq = pdata->irq;
+ new_bus->irq = create_irq_map(np);
+
+ if (new_bus->irq == NULL) {
+ err = -ENOMEM;
+ goto err_unmap_regs;
+ }
- new_bus->parent = dev;
- dev_set_drvdata(dev, new_bus);
+ new_bus->parent = &ofdev->dev;
+ dev_set_drvdata(&ofdev->dev, new_bus);
/*
* This is mildly evil, but so is our hardware for doing this.
@@ -206,96 +269,109 @@ static int gfar_mdio_probe(struct device *dev)
enet_regs = (struct gfar __iomem *)
((char *)regs - offsetof(struct gfar, gfar_mii_regs));
- /* Scan the bus, looking for an empty spot for TBIPA */
- gfar_write(&enet_regs->tbipa, 0);
- for (i = PHY_MAX_ADDR; i > 0; i--) {
- u32 phy_id;
+ for_each_child_of_node(np, tbi) {
+ if (!strncmp(tbi->type, "tbi-phy", 8))
+ break;
+ }
- err = get_phy_id(new_bus, i, &phy_id);
- if (err)
- goto bus_register_fail;
+ if (tbi) {
+ const u32 *prop = of_get_property(tbi, "reg", NULL);
- if (phy_id == 0xffffffff)
- break;
+ if (prop)
+ tbiaddr = *prop;
}
- /* The bus is full. We don't support using 31 PHYs, sorry */
- if (i == 0) {
+ if (tbiaddr == -1) {
+ gfar_write(&enet_regs->tbipa, 0);
+
+ tbiaddr = gfar_mdio_find_free(new_bus);
+ }
+
+ /*
+ * We define TBIPA at 0 to be illegal, opting to fail for boards that
+ * have PHYs at 1-31, rather than change tbipa and rescan.
+ */
+ if (tbiaddr == 0) {
err = -EBUSY;
- goto bus_register_fail;
+ goto err_free_irqs;
}
- gfar_write(&enet_regs->tbipa, i);
+ gfar_write(&enet_regs->tbipa, tbiaddr);
+
+ /*
+ * The TBIPHY-only buses will find PHYs at every address,
+ * so we mask them all but the TBI
+ */
+ if (!of_device_is_compatible(np, "fsl,gianfar-mdio"))
+ new_bus->phy_mask = ~(1 << tbiaddr);
err = mdiobus_register(new_bus);
- if (0 != err) {
+ if (err != 0) {
printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
new_bus->name);
- goto bus_register_fail;
+ goto err_free_irqs;
}
return 0;
-bus_register_fail:
+err_free_irqs:
+ kfree(new_bus->irq);
+err_unmap_regs:
iounmap(regs);
-reg_map_fail:
+err_free_bus:
mdiobus_free(new_bus);
return err;
}
-static int gfar_mdio_remove(struct device *dev)
+static int gfar_mdio_remove(struct of_device *ofdev)
{
- struct mii_bus *bus = dev_get_drvdata(dev);
+ struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
mdiobus_unregister(bus);
- dev_set_drvdata(dev, NULL);
+ dev_set_drvdata(&ofdev->dev, NULL);
iounmap((void __iomem *)bus->priv);
bus->priv = NULL;
+ kfree(bus->irq);
mdiobus_free(bus);
return 0;
}
-static struct device_driver gianfar_mdio_driver = {
+static struct of_device_id gfar_mdio_match[] =
+{
+ {
+ .compatible = "fsl,gianfar-mdio",
+ },
+ {
+ .compatible = "fsl,gianfar-tbi",
+ },
+ {
+ .type = "mdio",
+ .compatible = "gianfar",
+ },
+ {},
+};
+
+static struct of_platform_driver gianfar_mdio_driver = {
.name = "fsl-gianfar_mdio",
- .bus = &platform_bus_type,
+ .match_table = gfar_mdio_match,
+
.probe = gfar_mdio_probe,
.remove = gfar_mdio_remove,
};
-static int match_mdio_bus(struct device *dev, void *data)
-{
- const struct gfar_private *priv = data;
- const struct platform_device *pdev = to_platform_device(dev);
-
- return !strcmp(pdev->name, gianfar_mdio_driver.name) &&
- pdev->id == priv->einfo->mdio_bus;
-}
-
-/* Given a gfar_priv structure, find the mii_bus controlled by this device (not
- * necessarily the same as the bus the gfar's PHY is on), if one exists.
- * Normally only the first gianfar controls a mii_bus. */
-struct mii_bus *gfar_get_miibus(const struct gfar_private *priv)
-{
- /*const*/ struct device *d;
-
- d = bus_find_device(gianfar_mdio_driver.bus, NULL, (void *)priv,
- match_mdio_bus);
- return d ? dev_get_drvdata(d) : NULL;
-}
-
int __init gfar_mdio_init(void)
{
- return driver_register(&gianfar_mdio_driver);
+ return of_register_platform_driver(&gianfar_mdio_driver);
}
void gfar_mdio_exit(void)
{
- driver_unregister(&gianfar_mdio_driver);
+ of_unregister_platform_driver(&gianfar_mdio_driver);
}
diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h
index 02dc970ca1ff..65c242cd468a 100644
--- a/drivers/net/gianfar_mii.h
+++ b/drivers/net/gianfar_mii.h
@@ -49,4 +49,6 @@ int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum);
struct mii_bus *gfar_get_miibus(const struct gfar_private *priv);
int __init gfar_mdio_init(void);
void gfar_mdio_exit(void);
+
+void gfar_mdio_bus_name(char *name, struct device_node *np);
#endif /* GIANFAR_PHY_H */