1 // SPDX-License-Identifier: GPL-2.0 2 #define pr_fmt(fmt) "bcmasp_ethtool: " fmt 3 4 #include <linux/ethtool.h> 5 #include <linux/netdevice.h> 6 #include <linux/platform_device.h> 7 8 #include "bcmasp.h" 9 #include "bcmasp_intf_defs.h" 10 11 static void bcmasp_get_drvinfo(struct net_device *dev, 12 struct ethtool_drvinfo *info) 13 { 14 strscpy(info->driver, "bcmasp", sizeof(info->driver)); 15 strscpy(info->bus_info, dev_name(dev->dev.parent), 16 sizeof(info->bus_info)); 17 } 18 19 static u32 bcmasp_get_msglevel(struct net_device *dev) 20 { 21 struct bcmasp_intf *intf = netdev_priv(dev); 22 23 return intf->msg_enable; 24 } 25 26 static void bcmasp_set_msglevel(struct net_device *dev, u32 level) 27 { 28 struct bcmasp_intf *intf = netdev_priv(dev); 29 30 intf->msg_enable = level; 31 } 32 33 #define BCMASP_SUPPORTED_WAKE (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER) 34 static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 35 { 36 struct bcmasp_intf *intf = netdev_priv(dev); 37 38 wol->supported = BCMASP_SUPPORTED_WAKE; 39 wol->wolopts = intf->wolopts; 40 memset(wol->sopass, 0, sizeof(wol->sopass)); 41 42 if (wol->wolopts & WAKE_MAGICSECURE) 43 memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass)); 44 } 45 46 static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 47 { 48 struct bcmasp_intf *intf = netdev_priv(dev); 49 struct bcmasp_priv *priv = intf->parent; 50 struct device *kdev = &priv->pdev->dev; 51 52 if (!device_can_wakeup(kdev)) 53 return -EOPNOTSUPP; 54 55 /* Interface Specific */ 56 intf->wolopts = wol->wolopts; 57 if (intf->wolopts & WAKE_MAGICSECURE) 58 memcpy(intf->sopass, wol->sopass, sizeof(wol->sopass)); 59 60 mutex_lock(&priv->wol_lock); 61 priv->enable_wol(intf, !!intf->wolopts); 62 mutex_unlock(&priv->wol_lock); 63 64 return 0; 65 } 66 67 static int bcmasp_flow_insert(struct net_device *dev, struct ethtool_rxnfc *cmd) 68 { 69 struct bcmasp_intf *intf = netdev_priv(dev); 70 struct bcmasp_net_filter *nfilter; 71 u32 loc = cmd->fs.location; 72 bool wake = false; 73 74 if (cmd->fs.ring_cookie == RX_CLS_FLOW_WAKE) 75 wake = true; 76 77 /* Currently only supports WAKE filters */ 78 if (!wake) 79 return -EOPNOTSUPP; 80 81 switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 82 case ETHER_FLOW: 83 case IP_USER_FLOW: 84 case TCP_V4_FLOW: 85 case UDP_V4_FLOW: 86 case TCP_V6_FLOW: 87 case UDP_V6_FLOW: 88 break; 89 default: 90 return -EOPNOTSUPP; 91 } 92 93 /* Check if filter already exists */ 94 if (bcmasp_netfilt_check_dup(intf, &cmd->fs)) 95 return -EINVAL; 96 97 nfilter = bcmasp_netfilt_get_init(intf, loc, wake, true); 98 if (IS_ERR(nfilter)) 99 return PTR_ERR(nfilter); 100 101 /* Return the location where we did insert the filter */ 102 cmd->fs.location = nfilter->hw_index; 103 memcpy(&nfilter->fs, &cmd->fs, sizeof(struct ethtool_rx_flow_spec)); 104 105 /* Since we only support wake filters, defer register programming till 106 * suspend time. 107 */ 108 return 0; 109 } 110 111 static int bcmasp_flow_delete(struct net_device *dev, struct ethtool_rxnfc *cmd) 112 { 113 struct bcmasp_intf *intf = netdev_priv(dev); 114 struct bcmasp_net_filter *nfilter; 115 116 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 117 if (IS_ERR(nfilter)) 118 return PTR_ERR(nfilter); 119 120 bcmasp_netfilt_release(intf, nfilter); 121 122 return 0; 123 } 124 125 static int bcmasp_flow_get(struct bcmasp_intf *intf, struct ethtool_rxnfc *cmd) 126 { 127 struct bcmasp_net_filter *nfilter; 128 129 nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false); 130 if (IS_ERR(nfilter)) 131 return PTR_ERR(nfilter); 132 133 memcpy(&cmd->fs, &nfilter->fs, sizeof(nfilter->fs)); 134 135 cmd->data = NUM_NET_FILTERS; 136 137 return 0; 138 } 139 140 static int bcmasp_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 141 { 142 struct bcmasp_intf *intf = netdev_priv(dev); 143 int ret = -EOPNOTSUPP; 144 145 mutex_lock(&intf->parent->net_lock); 146 147 switch (cmd->cmd) { 148 case ETHTOOL_SRXCLSRLINS: 149 ret = bcmasp_flow_insert(dev, cmd); 150 break; 151 case ETHTOOL_SRXCLSRLDEL: 152 ret = bcmasp_flow_delete(dev, cmd); 153 break; 154 default: 155 break; 156 } 157 158 mutex_unlock(&intf->parent->net_lock); 159 160 return ret; 161 } 162 163 static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 164 u32 *rule_locs) 165 { 166 struct bcmasp_intf *intf = netdev_priv(dev); 167 int err = 0; 168 169 mutex_lock(&intf->parent->net_lock); 170 171 switch (cmd->cmd) { 172 case ETHTOOL_GRXCLSRLCNT: 173 cmd->rule_cnt = bcmasp_netfilt_get_active(intf); 174 /* We support specifying rule locations */ 175 cmd->data |= RX_CLS_LOC_SPECIAL; 176 break; 177 case ETHTOOL_GRXCLSRULE: 178 err = bcmasp_flow_get(intf, cmd); 179 break; 180 case ETHTOOL_GRXCLSRLALL: 181 bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); 182 cmd->data = NUM_NET_FILTERS; 183 break; 184 default: 185 err = -EOPNOTSUPP; 186 break; 187 } 188 189 mutex_unlock(&intf->parent->net_lock); 190 191 return err; 192 } 193 194 void bcmasp_eee_enable_set(struct bcmasp_intf *intf, bool enable) 195 { 196 u32 reg; 197 198 reg = umac_rl(intf, UMC_EEE_CTRL); 199 if (enable) 200 reg |= EEE_EN; 201 else 202 reg &= ~EEE_EN; 203 umac_wl(intf, reg, UMC_EEE_CTRL); 204 205 intf->eee.eee_enabled = enable; 206 intf->eee.eee_active = enable; 207 } 208 209 static int bcmasp_get_eee(struct net_device *dev, struct ethtool_eee *e) 210 { 211 struct bcmasp_intf *intf = netdev_priv(dev); 212 struct ethtool_eee *p = &intf->eee; 213 214 if (!dev->phydev) 215 return -ENODEV; 216 217 e->eee_enabled = p->eee_enabled; 218 e->eee_active = p->eee_active; 219 e->tx_lpi_enabled = p->tx_lpi_enabled; 220 e->tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER); 221 222 return phy_ethtool_get_eee(dev->phydev, e); 223 } 224 225 static int bcmasp_set_eee(struct net_device *dev, struct ethtool_eee *e) 226 { 227 struct bcmasp_intf *intf = netdev_priv(dev); 228 struct ethtool_eee *p = &intf->eee; 229 int ret; 230 231 if (!dev->phydev) 232 return -ENODEV; 233 234 if (!p->eee_enabled) { 235 bcmasp_eee_enable_set(intf, false); 236 } else { 237 ret = phy_init_eee(dev->phydev, 0); 238 if (ret) { 239 netif_err(intf, hw, dev, 240 "EEE initialization failed: %d\n", ret); 241 return ret; 242 } 243 244 umac_wl(intf, e->tx_lpi_timer, UMC_EEE_LPI_TIMER); 245 intf->eee.eee_active = ret >= 0; 246 intf->eee.tx_lpi_enabled = e->tx_lpi_enabled; 247 bcmasp_eee_enable_set(intf, true); 248 } 249 250 return phy_ethtool_set_eee(dev->phydev, e); 251 } 252 253 const struct ethtool_ops bcmasp_ethtool_ops = { 254 .get_drvinfo = bcmasp_get_drvinfo, 255 .get_link = ethtool_op_get_link, 256 .get_link_ksettings = phy_ethtool_get_link_ksettings, 257 .set_link_ksettings = phy_ethtool_set_link_ksettings, 258 .get_msglevel = bcmasp_get_msglevel, 259 .set_msglevel = bcmasp_set_msglevel, 260 .get_wol = bcmasp_get_wol, 261 .set_wol = bcmasp_set_wol, 262 .get_rxnfc = bcmasp_get_rxnfc, 263 .set_rxnfc = bcmasp_set_rxnfc, 264 .set_eee = bcmasp_set_eee, 265 .get_eee = bcmasp_get_eee, 266 }; 267