xref: /openbmc/linux/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c (revision c5d511c4958780338ac93f3e728012d5e17de710)
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 const struct ethtool_ops bcmasp_ethtool_ops = {
195 	.get_drvinfo		= bcmasp_get_drvinfo,
196 	.get_link		= ethtool_op_get_link,
197 	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
198 	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
199 	.get_msglevel		= bcmasp_get_msglevel,
200 	.set_msglevel		= bcmasp_set_msglevel,
201 	.get_wol		= bcmasp_get_wol,
202 	.set_wol		= bcmasp_set_wol,
203 	.get_rxnfc		= bcmasp_get_rxnfc,
204 	.set_rxnfc		= bcmasp_set_rxnfc,
205 };
206