diff options
-rw-r--r-- | drivers/net/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/pasemi_mac.c | 126 | ||||
-rw-r--r-- | drivers/net/pasemi_mac.h | 6 |
3 files changed, 133 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 372f25716b5..b86ccd2ecd5 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2488,6 +2488,7 @@ config NETXEN_NIC config PASEMI_MAC tristate "PA Semi 1/10Gbit MAC" depends on PPC64 && PCI + select PHYLIB help This driver supports the on-chip 1/10Gbit Ethernet controller on PA Semi's PWRficient line of chips. diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index 4c6d34d6989..78b127c404e 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -606,6 +606,114 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data) return IRQ_HANDLED; } +static void pasemi_adjust_link(struct net_device *dev) +{ + struct pasemi_mac *mac = netdev_priv(dev); + int msg; + unsigned int flags; + unsigned int new_flags; + + if (!mac->phydev->link) { + /* If no link, MAC speed settings don't matter. Just report + * link down and return. + */ + if (mac->link && netif_msg_link(mac)) + printk(KERN_INFO "%s: Link is down.\n", dev->name); + + netif_carrier_off(dev); + mac->link = 0; + + return; + } else + netif_carrier_on(dev); + + pci_read_config_dword(mac->pdev, PAS_MAC_CFG_PCFG, &flags); + new_flags = flags & ~(PAS_MAC_CFG_PCFG_HD | PAS_MAC_CFG_PCFG_SPD_M | + PAS_MAC_CFG_PCFG_TSR_M); + + if (!mac->phydev->duplex) + new_flags |= PAS_MAC_CFG_PCFG_HD; + + switch (mac->phydev->speed) { + case 1000: + new_flags |= PAS_MAC_CFG_PCFG_SPD_1G | + PAS_MAC_CFG_PCFG_TSR_1G; + break; + case 100: + new_flags |= PAS_MAC_CFG_PCFG_SPD_100M | + PAS_MAC_CFG_PCFG_TSR_100M; + break; + case 10: + new_flags |= PAS_MAC_CFG_PCFG_SPD_10M | + PAS_MAC_CFG_PCFG_TSR_10M; + break; + default: + printk("Unsupported speed %d\n", mac->phydev->speed); + } + + /* Print on link or speed/duplex change */ + msg = mac->link != mac->phydev->link || flags != new_flags; + + mac->duplex = mac->phydev->duplex; + mac->speed = mac->phydev->speed; + mac->link = mac->phydev->link; + + if (new_flags != flags) + pci_write_config_dword(mac->pdev, PAS_MAC_CFG_PCFG, new_flags); + + if (msg && netif_msg_link(mac)) + printk(KERN_INFO "%s: Link is up at %d Mbps, %s duplex.\n", + dev->name, mac->speed, mac->duplex ? "full" : "half"); +} + +static int pasemi_mac_phy_init(struct net_device *dev) +{ + struct pasemi_mac *mac = netdev_priv(dev); + struct device_node *dn, *phy_dn; + struct phy_device *phydev; + unsigned int phy_id; + const phandle *ph; + const unsigned int *prop; + struct resource r; + int ret; + + dn = pci_device_to_OF_node(mac->pdev); + ph = get_property(dn, "phy-handle", NULL); + if (!ph) + return -ENODEV; + phy_dn = of_find_node_by_phandle(*ph); + + prop = get_property(phy_dn, "reg", NULL); + ret = of_address_to_resource(phy_dn->parent, 0, &r); + if (ret) + goto err; + + phy_id = *prop; + snprintf(mac->phy_id, BUS_ID_SIZE, PHY_ID_FMT, (int)r.start, phy_id); + + of_node_put(phy_dn); + + mac->link = 0; + mac->speed = 0; + mac->duplex = -1; + + phydev = phy_connect(dev, mac->phy_id, &pasemi_adjust_link, 0, PHY_INTERFACE_MODE_SGMII); + + if (IS_ERR(phydev)) { + printk(KERN_ERR "%s: Could not attach to phy\n", dev->name); + return PTR_ERR(phydev); + } + + mac->phydev = phydev; + + return 0; + +err: + of_node_put(phy_dn); + return -ENODEV; +} + + static int pasemi_mac_open(struct net_device *dev) { struct pasemi_mac *mac = netdev_priv(dev); @@ -678,6 +786,13 @@ static int pasemi_mac_open(struct net_device *dev) pasemi_mac_replenish_rx_ring(dev); + ret = pasemi_mac_phy_init(dev); + /* Some configs don't have PHYs (XAUI etc), so don't complain about + * failed init due to -ENODEV. + */ + if (ret && ret != -ENODEV) + dev_warn(&mac->pdev->dev, "phy init failed: %d\n", ret); + netif_start_queue(dev); netif_poll_enable(dev); @@ -708,6 +823,9 @@ static int pasemi_mac_open(struct net_device *dev) goto out_rx_int; } + if (mac->phydev) + phy_start(mac->phydev); + return 0; out_rx_int: @@ -731,6 +849,11 @@ static int pasemi_mac_close(struct net_device *dev) unsigned int stat; int retries; + if (mac->phydev) { + phy_stop(mac->phydev); + phy_disconnect(mac->phydev); + } + netif_stop_queue(dev); /* Clean out any pending buffers */ @@ -1028,6 +1151,9 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mac->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + /* Enable most messages by default */ + mac->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; + err = register_netdev(dev); if (err) { diff --git a/drivers/net/pasemi_mac.h b/drivers/net/pasemi_mac.h index 6539de1c2f2..8bc0cea8b14 100644 --- a/drivers/net/pasemi_mac.h +++ b/drivers/net/pasemi_mac.h @@ -24,6 +24,7 @@ #include <linux/ethtool.h> #include <linux/netdevice.h> #include <linux/spinlock.h> +#include <linux/phy.h> struct pasemi_mac_txring { spinlock_t lock; @@ -54,6 +55,7 @@ struct pasemi_mac { struct pci_dev *pdev; struct pci_dev *dma_pdev; struct pci_dev *iob_pdev; + struct phy_device *phydev; struct net_device_stats stats; /* Pointer to the cacheable per-channel status registers */ @@ -75,8 +77,12 @@ struct pasemi_mac { struct pasemi_mac_rxring *rx; unsigned long tx_irq; unsigned long rx_irq; + int link; + int speed; + int duplex; unsigned int msg_enable; + char phy_id[BUS_ID_SIZE]; }; /* Software status descriptor (desc_info) */ |