xref: /openbmc/linux/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c (revision 550e6f345687253083b289b167651deb42869032)
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