#include #include #include #include #include "gsc3280mac.h" #include "gsc3280mac_dma.h" #define REG_SPACE_SIZE 0x1054 #define MAC100_ETHTOOL_NAME "st_mac100" #define GSC3280MAC_ETHTOOL_NAME "st_gmac" struct gsc3280mac_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; int stat_offset; }; #define GSC3280MAC_STAT(m) \ { #m, FIELD_SIZEOF(struct gsc3280mac_extra_stats, m), \ offsetof(struct gsc3280mac_priv, xstats.m)} static const struct gsc3280mac_stats gsc3280mac_gstrings_stats[] = { GSC3280MAC_STAT(tx_underflow), GSC3280MAC_STAT(tx_carrier), GSC3280MAC_STAT(tx_losscarrier), GSC3280MAC_STAT(tx_heartbeat), GSC3280MAC_STAT(tx_deferred), GSC3280MAC_STAT(tx_vlan), GSC3280MAC_STAT(rx_vlan), GSC3280MAC_STAT(tx_jabber), GSC3280MAC_STAT(tx_frame_flushed), GSC3280MAC_STAT(tx_payload_error), GSC3280MAC_STAT(tx_ip_header_error), GSC3280MAC_STAT(rx_desc), GSC3280MAC_STAT(rx_partial), GSC3280MAC_STAT(rx_runt), GSC3280MAC_STAT(rx_toolong), GSC3280MAC_STAT(rx_collision), GSC3280MAC_STAT(rx_crc), GSC3280MAC_STAT(rx_length), GSC3280MAC_STAT(rx_mii), GSC3280MAC_STAT(rx_multicast), GSC3280MAC_STAT(rx_gmac_overflow), GSC3280MAC_STAT(rx_watchdog), GSC3280MAC_STAT(da_rx_filter_fail), GSC3280MAC_STAT(sa_rx_filter_fail), GSC3280MAC_STAT(rx_missed_cntr), GSC3280MAC_STAT(rx_overflow_cntr), GSC3280MAC_STAT(tx_undeflow_irq), GSC3280MAC_STAT(tx_process_stopped_irq), GSC3280MAC_STAT(tx_jabber_irq), GSC3280MAC_STAT(rx_overflow_irq), GSC3280MAC_STAT(rx_buf_unav_irq), GSC3280MAC_STAT(rx_process_stopped_irq), GSC3280MAC_STAT(rx_watchdog_irq), GSC3280MAC_STAT(tx_early_irq), GSC3280MAC_STAT(fatal_bus_error_irq), GSC3280MAC_STAT(threshold), GSC3280MAC_STAT(tx_pkt_n), GSC3280MAC_STAT(rx_pkt_n), GSC3280MAC_STAT(poll_n), GSC3280MAC_STAT(sched_timer_n), GSC3280MAC_STAT(normal_irq_n), }; #define GSC3280MAC_STATS_LEN ARRAY_SIZE(gsc3280mac_gstrings_stats) static void gsc3280mac_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { strcpy(info->driver, GSC3280MAC_ETHTOOL_NAME); strcpy(info->version, DRV_MODULE_VERSION); info->fw_version[0] = '\0'; info->n_stats = GSC3280MAC_STATS_LEN; } static int gsc3280mac_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct gsc3280mac_priv *priv = netdev_priv(dev); struct phy_device *phy = priv->phydev; int rc; if (phy == NULL) { pr_err("%s: %s: PHY is not registered\n", __func__, dev->name); return -ENODEV; } if (!netif_running(dev)) { pr_err("%s: interface is disabled: we cannot track " "link speed / duplex setting\n", dev->name); return -EBUSY; } cmd->transceiver = XCVR_INTERNAL; spin_lock_irq(&priv->lock); rc = phy_ethtool_gset(phy, cmd); spin_unlock_irq(&priv->lock); return rc; } static int gsc3280mac_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) { struct gsc3280mac_priv *priv = netdev_priv(dev); struct phy_device *phy = priv->phydev; int rc; spin_lock(&priv->lock); rc = phy_ethtool_sset(phy, cmd); spin_unlock(&priv->lock); return rc; } static u32 gsc3280mac_ethtool_getmsglevel(struct net_device *dev) { struct gsc3280mac_priv *priv = netdev_priv(dev); return priv->msg_enable; } static void gsc3280mac_ethtool_setmsglevel(struct net_device *dev, u32 level) { struct gsc3280mac_priv *priv = netdev_priv(dev); priv->msg_enable = level; } static int gsc3280mac_check_if_running(struct net_device *dev) { if (!netif_running(dev)) return -EBUSY; return 0; } static int gsc3280mac_ethtool_get_regs_len(struct net_device *dev) { return REG_SPACE_SIZE; } static void gsc3280mac_ethtool_gregs(struct net_device *dev, struct ethtool_regs *regs, void *space) { int i; u32 *reg_space = (u32 *) space; struct gsc3280mac_priv *priv = netdev_priv(dev); memset(reg_space, 0x0, REG_SPACE_SIZE); /* MAC registers */ for (i = 0; i < 55; i++) reg_space[i] = readl(priv->ioaddr + (i * 4)); /* DMA registers */ for (i = 0; i < 22; i++) reg_space[i + 55] = readl(priv->ioaddr + (DMA_BUS_MODE + (i * 4))); } static void gsc3280mac_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct gsc3280mac_priv *priv = netdev_priv(netdev); spin_lock(&priv->lock); pause->rx_pause = 0; pause->tx_pause = 0; pause->autoneg = priv->phydev->autoneg; if (priv->flow_ctrl & FLOW_RX) pause->rx_pause = 1; if (priv->flow_ctrl & FLOW_TX) pause->tx_pause = 1; spin_unlock(&priv->lock); } static int gsc3280mac_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct gsc3280mac_priv *priv = netdev_priv(netdev); struct phy_device *phy = priv->phydev; int new_pause = FLOW_OFF; int ret = 0; spin_lock(&priv->lock); if (pause->rx_pause) new_pause |= FLOW_RX; if (pause->tx_pause) new_pause |= FLOW_TX; priv->flow_ctrl = new_pause; phy->autoneg = pause->autoneg; if (phy->autoneg) { if (netif_running(netdev)) ret = phy_start_aneg(phy); } else priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex, priv->flow_ctrl, priv->pause); spin_unlock(&priv->lock); return ret; } static void gsc3280mac_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 *data) { struct gsc3280mac_priv *priv = netdev_priv(dev); int i; /* Update HW stats if supported */ priv->hw->dma->dma_diagnostic_fr(&dev->stats, (void *) &priv->xstats, priv->ioaddr); for (i = 0; i < GSC3280MAC_STATS_LEN; i++) { char *p = (char *)priv + gsc3280mac_gstrings_stats[i].stat_offset; data[i] = (gsc3280mac_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? (*(u64 *)p) : (*(u32 *)p); } } static int gsc3280mac_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_STATS: return GSC3280MAC_STATS_LEN; default: return -EOPNOTSUPP; } } static void gsc3280mac_get_strings(struct net_device *dev, u32 stringset, u8 *data) { int i; u8 *p = data; switch (stringset) { case ETH_SS_STATS: for (i = 0; i < GSC3280MAC_STATS_LEN; i++) { memcpy(p, gsc3280mac_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } break; default: WARN_ON(1); break; } } /* Currently only support WOL through Magic packet. */ static void gsc3280mac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gsc3280mac_priv *priv = netdev_priv(dev); spin_lock_irq(&priv->lock); if (device_can_wakeup(priv->device)) { wol->supported = WAKE_MAGIC | WAKE_UCAST; wol->wolopts = priv->wolopts; } spin_unlock_irq(&priv->lock); } static int gsc3280mac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gsc3280mac_priv *priv = netdev_priv(dev); u32 support = WAKE_MAGIC | WAKE_UCAST; if (!device_can_wakeup(priv->device)) return -EINVAL; if (wol->wolopts & ~support) return -EINVAL; if (wol->wolopts) { pr_info("gsc3280mac: wakeup enable\n"); device_set_wakeup_enable(priv->device, 1); enable_irq_wake(dev->irq); } else { device_set_wakeup_enable(priv->device, 0); disable_irq_wake(dev->irq); } spin_lock_irq(&priv->lock); priv->wolopts = wol->wolopts; spin_unlock_irq(&priv->lock); return 0; } static struct ethtool_ops gsc3280mac_ethtool_ops = { .begin = gsc3280mac_check_if_running, .get_drvinfo = gsc3280mac_ethtool_getdrvinfo, .get_settings = gsc3280mac_ethtool_getsettings, .set_settings = gsc3280mac_ethtool_setsettings, .get_msglevel = gsc3280mac_ethtool_getmsglevel, .set_msglevel = gsc3280mac_ethtool_setmsglevel, .get_regs = gsc3280mac_ethtool_gregs, .get_regs_len = gsc3280mac_ethtool_get_regs_len, .get_link = ethtool_op_get_link, .get_pauseparam = gsc3280mac_get_pauseparam, .set_pauseparam = gsc3280mac_set_pauseparam, .get_ethtool_stats = gsc3280mac_get_ethtool_stats, .get_strings = gsc3280mac_get_strings, .get_wol = gsc3280mac_get_wol, .set_wol = gsc3280mac_set_wol, .get_sset_count = gsc3280mac_get_sset_count, }; void gsc3280mac_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &gsc3280mac_ethtool_ops); }