diff options
Diffstat (limited to 'drivers/net/ethernet/faraday/ftgmac100.c')
-rw-r--r-- | drivers/net/ethernet/faraday/ftgmac100.c | 271 |
1 files changed, 208 insertions, 63 deletions
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index e7cf313e359b..36361f8bf894 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -31,6 +31,7 @@ #include <linux/phy.h> #include <linux/platform_device.h> #include <net/ip.h> +#include <net/ncsi.h> #include "ftgmac100.h" @@ -68,10 +69,14 @@ struct ftgmac100 { struct net_device *netdev; struct device *dev; + struct ncsi_dev *ndev; struct napi_struct napi; struct mii_bus *mii_bus; int old_speed; + int int_mask_all; + bool use_ncsi; + bool enabled; }; static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, @@ -80,14 +85,6 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, /****************************************************************************** * internal functions (hardware register access) *****************************************************************************/ -#define INT_MASK_ALL_ENABLED (FTGMAC100_INT_RPKT_LOST | \ - FTGMAC100_INT_XPKT_ETH | \ - FTGMAC100_INT_XPKT_LOST | \ - FTGMAC100_INT_AHB_ERR | \ - FTGMAC100_INT_PHYSTS_CHG | \ - FTGMAC100_INT_RPKT_BUF | \ - FTGMAC100_INT_NO_RXBUF) - static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) { iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); @@ -141,6 +138,55 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac) iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR); } +static void ftgmac100_setup_mac(struct ftgmac100 *priv) +{ + u8 mac[ETH_ALEN]; + unsigned int m; + unsigned int l; + void *addr; + + addr = device_get_mac_address(priv->dev, mac, ETH_ALEN); + if (addr) { + ether_addr_copy(priv->netdev->dev_addr, mac); + dev_info(priv->dev, "Read MAC address %pM from device tree\n", + mac); + return; + } + + m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR); + l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR); + + mac[0] = (m >> 8) & 0xff; + mac[1] = m & 0xff; + mac[2] = (l >> 24) & 0xff; + mac[3] = (l >> 16) & 0xff; + mac[4] = (l >> 8) & 0xff; + mac[5] = l & 0xff; + + if (is_valid_ether_addr(mac)) { + ether_addr_copy(priv->netdev->dev_addr, mac); + dev_info(priv->dev, "Read MAC address %pM from chip\n", mac); + } else { + eth_hw_addr_random(priv->netdev); + dev_info(priv->dev, "Generated random MAC address %pM\n", + priv->netdev->dev_addr); + } +} + +static int ftgmac100_set_mac_addr(struct net_device *dev, void *p) +{ + int ret; + + ret = eth_prepare_mac_addr_change(dev, p); + if (ret < 0) + return ret; + + eth_commit_mac_addr_change(dev, p); + ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr); + + return 0; +} + static void ftgmac100_init_hw(struct ftgmac100 *priv) { /* setup ring buffer base registers */ @@ -952,7 +998,10 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) struct net_device *netdev = dev_id; struct ftgmac100 *priv = netdev_priv(netdev); - if (likely(netif_running(netdev))) { + /* When running in NCSI mode, the interface should be ready for + * receiving or transmitting NCSI packets before it's opened. + */ + if (likely(priv->use_ncsi || netif_running(netdev))) { /* Disable interrupts for polling */ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); napi_schedule(&priv->napi); @@ -1005,8 +1054,9 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) ftgmac100_tx_complete(priv); } - if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST | - FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) { + if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF | + FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR | + FTGMAC100_INT_PHYSTS_CHG)) { if (net_ratelimit()) netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "", @@ -1029,7 +1079,8 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) napi_complete(napi); /* enable all interrupts */ - iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); + iowrite32(priv->int_mask_all, + priv->base + FTGMAC100_OFFSET_IER); } return rx; @@ -1065,17 +1116,33 @@ static int ftgmac100_open(struct net_device *netdev) goto err_hw; ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv, 10); - - phy_start(netdev->phydev); + ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10); + if (netdev->phydev) + phy_start(netdev->phydev); + else if (priv->use_ncsi) + netif_carrier_on(netdev); napi_enable(&priv->napi); netif_start_queue(netdev); /* enable all interrupts */ - iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); + iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER); + + /* Start the NCSI device */ + if (priv->use_ncsi) { + err = ncsi_start_dev(priv->ndev); + if (err) + goto err_ncsi; + } + + priv->enabled = true; + return 0; +err_ncsi: + napi_disable(&priv->napi); + netif_stop_queue(netdev); + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); err_hw: free_irq(priv->irq, netdev); err_irq: @@ -1088,12 +1155,17 @@ static int ftgmac100_stop(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); + if (!priv->enabled) + return 0; + /* disable all interrupts */ + priv->enabled = false; iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); netif_stop_queue(netdev); napi_disable(&priv->napi); - phy_stop(netdev->phydev); + if (netdev->phydev) + phy_stop(netdev->phydev); ftgmac100_stop_hw(priv); free_irq(priv->irq, netdev); @@ -1134,6 +1206,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* optional */ static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { + if (!netdev->phydev) + return -ENXIO; + return phy_mii_ioctl(netdev->phydev, ifr, cmd); } @@ -1141,11 +1216,74 @@ static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_open = ftgmac100_open, .ndo_stop = ftgmac100_stop, .ndo_start_xmit = ftgmac100_hard_start_xmit, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = ftgmac100_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = ftgmac100_do_ioctl, }; +static int ftgmac100_setup_mdio(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + struct platform_device *pdev = to_platform_device(priv->dev); + int i, err = 0; + + /* initialize mdio bus */ + priv->mii_bus = mdiobus_alloc(); + if (!priv->mii_bus) + return -EIO; + + priv->mii_bus->name = "ftgmac100_mdio"; + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d", + pdev->name, pdev->id); + priv->mii_bus->priv = priv->netdev; + priv->mii_bus->read = ftgmac100_mdiobus_read; + priv->mii_bus->write = ftgmac100_mdiobus_write; + + for (i = 0; i < PHY_MAX_ADDR; i++) + priv->mii_bus->irq[i] = PHY_POLL; + + err = mdiobus_register(priv->mii_bus); + if (err) { + dev_err(priv->dev, "Cannot register MDIO bus!\n"); + goto err_register_mdiobus; + } + + err = ftgmac100_mii_probe(priv); + if (err) { + dev_err(priv->dev, "MII Probe failed!\n"); + goto err_mii_probe; + } + + return 0; + +err_mii_probe: + mdiobus_unregister(priv->mii_bus); +err_register_mdiobus: + mdiobus_free(priv->mii_bus); + return err; +} + +static void ftgmac100_destroy_mdio(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + if (!netdev->phydev) + return; + + phy_disconnect(netdev->phydev); + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); +} + +static void ftgmac100_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_info(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + /****************************************************************************** * struct platform_driver functions *****************************************************************************/ @@ -1155,7 +1293,7 @@ static int ftgmac100_probe(struct platform_device *pdev) int irq; struct net_device *netdev; struct ftgmac100 *priv; - int err; + int err = 0; if (!pdev) return -ENODEV; @@ -1179,7 +1317,6 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev->ethtool_ops = &ftgmac100_ethtool_ops; netdev->netdev_ops = &ftgmac100_netdev_ops; - netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; platform_set_drvdata(pdev, netdev); @@ -1211,31 +1348,45 @@ static int ftgmac100_probe(struct platform_device *pdev) priv->irq = irq; - /* initialize mdio bus */ - priv->mii_bus = mdiobus_alloc(); - if (!priv->mii_bus) { - err = -EIO; - goto err_alloc_mdiobus; - } - - priv->mii_bus->name = "ftgmac100_mdio"; - snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii"); - - priv->mii_bus->priv = netdev; - priv->mii_bus->read = ftgmac100_mdiobus_read; - priv->mii_bus->write = ftgmac100_mdiobus_write; + /* MAC address from chip or random one */ + ftgmac100_setup_mac(priv); + + priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST | + FTGMAC100_INT_XPKT_ETH | + FTGMAC100_INT_XPKT_LOST | + FTGMAC100_INT_AHB_ERR | + FTGMAC100_INT_PHYSTS_CHG | + FTGMAC100_INT_RPKT_BUF | + FTGMAC100_INT_NO_RXBUF); + if (pdev->dev.of_node && + of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) { + if (!IS_ENABLED(CONFIG_NET_NCSI)) { + dev_err(&pdev->dev, "NCSI stack not enabled\n"); + goto err_ncsi_dev; + } - err = mdiobus_register(priv->mii_bus); - if (err) { - dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); - goto err_register_mdiobus; + dev_info(&pdev->dev, "Using NCSI interface\n"); + priv->use_ncsi = true; + priv->int_mask_all &= ~FTGMAC100_INT_PHYSTS_CHG; + priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler); + if (!priv->ndev) + goto err_ncsi_dev; + } else { + priv->use_ncsi = false; + err = ftgmac100_setup_mdio(netdev); + if (err) + goto err_setup_mdio; } - err = ftgmac100_mii_probe(priv); - if (err) { - dev_err(&pdev->dev, "MII Probe failed!\n"); - goto err_mii_probe; - } + /* We have to disable on-chip IP checksum functionality + * when NCSI is enabled on the interface. It doesn't work + * in that case. + */ + netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; + if (priv->use_ncsi && + of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) + netdev->features &= ~NETIF_F_IP_CSUM; + /* register network device */ err = register_netdev(netdev); @@ -1246,21 +1397,12 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); - if (!is_valid_ether_addr(netdev->dev_addr)) { - eth_hw_addr_random(netdev); - netdev_info(netdev, "generated random MAC address %pM\n", - netdev->dev_addr); - } - return 0; +err_ncsi_dev: err_register_netdev: - phy_disconnect(netdev->phydev); -err_mii_probe: - mdiobus_unregister(priv->mii_bus); -err_register_mdiobus: - mdiobus_free(priv->mii_bus); -err_alloc_mdiobus: + ftgmac100_destroy_mdio(netdev); +err_setup_mdio: iounmap(priv->base); err_ioremap: release_resource(priv->res); @@ -1280,10 +1422,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev) priv = netdev_priv(netdev); unregister_netdev(netdev); - - phy_disconnect(netdev->phydev); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + ftgmac100_destroy_mdio(netdev); iounmap(priv->base); release_resource(priv->res); @@ -1293,14 +1432,20 @@ static int __exit ftgmac100_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ftgmac100_of_match[] = { + { .compatible = "faraday,ftgmac100" }, + { } +}; +MODULE_DEVICE_TABLE(of, ftgmac100_of_match); + static struct platform_driver ftgmac100_driver = { - .probe = ftgmac100_probe, - .remove = __exit_p(ftgmac100_remove), - .driver = { - .name = DRV_NAME, + .probe = ftgmac100_probe, + .remove = __exit_p(ftgmac100_remove), + .driver = { + .name = DRV_NAME, + .of_match_table = ftgmac100_of_match, }, }; - module_platform_driver(ftgmac100_driver); MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>"); |