File Index
Files and directories
Sign in
FILE

bcmasp_ethtool.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c C
  1// SPDX-License-Identifier: GPL-2.0
  2#define pr_fmt(fmt)				"bcmasp_ethtool: " fmt
  3
  4#include <linux/unaligned.h>
  5#include <linux/ethtool.h>
  6#include <linux/netdevice.h>
  7#include <linux/platform_device.h>
  8
  9#include "bcmasp.h"
 10#include "bcmasp_intf_defs.h"
 11
 12enum bcmasp_stat_type {
 13	BCMASP_STAT_RX_CTRL,
 14	BCMASP_STAT_RX_CTRL_PER_INTF,
 15	BCMASP_STAT_SOFT,
 16};
 17
 18struct bcmasp_stats {
 19	char stat_string[ETH_GSTRING_LEN];
 20	enum bcmasp_stat_type type;
 21	u32 reg_offset;
 22};
 23
 24#define STAT_BCMASP_SOFT_MIB(str) { \
 25	.stat_string = str, \
 26	.type = BCMASP_STAT_SOFT, \
 27}
 28
 29#define STAT_BCMASP_OFFSET(str, _type, offset) { \
 30	.stat_string = str, \
 31	.type = _type, \
 32	.reg_offset = offset, \
 33}
 34
 35#define STAT_BCMASP_RX_CTRL(str, offset) \
 36	STAT_BCMASP_OFFSET(str, BCMASP_STAT_RX_CTRL, offset)
 37#define STAT_BCMASP_RX_CTRL_PER_INTF(str, offset) \
 38	STAT_BCMASP_OFFSET(str, BCMASP_STAT_RX_CTRL_PER_INTF, offset)
 39
 40/* Must match the order of struct bcmasp_mib_counters */
 41static const struct bcmasp_stats bcmasp_gstrings_stats[] = {
 42	/* ASP RX control */
 43	STAT_BCMASP_RX_CTRL_PER_INTF("Frames From Unimac",
 44				     ASP_RX_CTRL_UMAC_0_FRAME_COUNT),
 45	STAT_BCMASP_RX_CTRL_PER_INTF("Frames From Port",
 46				     ASP_RX_CTRL_FB_0_FRAME_COUNT),
 47	STAT_BCMASP_RX_CTRL_PER_INTF("RX Buffer FIFO Depth",
 48				     ASP_RX_CTRL_FB_RX_FIFO_DEPTH),
 49	STAT_BCMASP_RX_CTRL("Frames Out(Buffer)",
 50			    ASP_RX_CTRL_FB_OUT_FRAME_COUNT),
 51	STAT_BCMASP_RX_CTRL("Frames Out(Filters)",
 52			    ASP_RX_CTRL_FB_FILT_OUT_FRAME_COUNT),
 53	/* Software maintained statistics */
 54	STAT_BCMASP_SOFT_MIB("RX SKB Alloc Failed"),
 55	STAT_BCMASP_SOFT_MIB("TX DMA Failed"),
 56	STAT_BCMASP_SOFT_MIB("Multicast Filters Full"),
 57	STAT_BCMASP_SOFT_MIB("Unicast Filters Full"),
 58	STAT_BCMASP_SOFT_MIB("MDA Filters Combined"),
 59	STAT_BCMASP_SOFT_MIB("Promisc Filter Set"),
 60	STAT_BCMASP_SOFT_MIB("TX Realloc For Offload Failed"),
 61	STAT_BCMASP_SOFT_MIB("Tx Timeout Count"),
 62};
 63
 64#define BCMASP_STATS_LEN	ARRAY_SIZE(bcmasp_gstrings_stats)
 65
 66static int bcmasp_get_sset_count(struct net_device *dev, int string_set)
 67{
 68	switch (string_set) {
 69	case ETH_SS_STATS:
 70		return BCMASP_STATS_LEN;
 71	default:
 72		return -EOPNOTSUPP;
 73	}
 74}
 75
 76static void bcmasp_get_strings(struct net_device *dev, u32 stringset,
 77			       u8 *data)
 78{
 79	const char *str;
 80	unsigned int i;
 81
 82	switch (stringset) {
 83	case ETH_SS_STATS:
 84		for (i = 0; i < BCMASP_STATS_LEN; i++) {
 85			str = bcmasp_gstrings_stats[i].stat_string;
 86			ethtool_puts(&data, str);
 87		}
 88		break;
 89	default:
 90		return;
 91	}
 92}
 93
 94static void bcmasp_update_mib_counters(struct bcmasp_intf *intf)
 95{
 96	unsigned int i;
 97
 98	for (i = 0; i < BCMASP_STATS_LEN; i++) {
 99		const struct bcmasp_stats *s;
100		u32 offset, val;
101		char *p;
102
103		s = &bcmasp_gstrings_stats[i];
104		offset = s->reg_offset;
105		switch (s->type) {
106		case BCMASP_STAT_SOFT:
107			continue;
108		case BCMASP_STAT_RX_CTRL:
109			val = rx_ctrl_core_rl(intf->parent, offset);
110			break;
111		case BCMASP_STAT_RX_CTRL_PER_INTF:
112			offset += sizeof(u32) * intf->port;
113			val = rx_ctrl_core_rl(intf->parent, offset);
114			break;
115		default:
116			continue;
117		}
118		p = (char *)(&intf->mib) + (i * sizeof(u32));
119		put_unaligned(val, (u32 *)p);
120	}
121}
122
123static void bcmasp_get_ethtool_stats(struct net_device *dev,
124				     struct ethtool_stats *stats,
125				     u64 *data)
126{
127	struct bcmasp_intf *intf = netdev_priv(dev);
128	unsigned int i;
129	char *p;
130
131	if (netif_running(dev))
132		bcmasp_update_mib_counters(intf);
133
134	for (i = 0; i < BCMASP_STATS_LEN; i++) {
135		p = (char *)(&intf->mib) + (i * sizeof(u32));
136		data[i] = *(u32 *)p;
137	}
138}
139
140static void bcmasp_get_drvinfo(struct net_device *dev,
141			       struct ethtool_drvinfo *info)
142{
143	strscpy(info->driver, "bcmasp", sizeof(info->driver));
144	strscpy(info->bus_info, dev_name(dev->dev.parent),
145		sizeof(info->bus_info));
146}
147
148static u32 bcmasp_get_msglevel(struct net_device *dev)
149{
150	struct bcmasp_intf *intf = netdev_priv(dev);
151
152	return intf->msg_enable;
153}
154
155static void bcmasp_set_msglevel(struct net_device *dev, u32 level)
156{
157	struct bcmasp_intf *intf = netdev_priv(dev);
158
159	intf->msg_enable = level;
160}
161
162#define BCMASP_SUPPORTED_WAKE   (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER)
163static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
164{
165	struct bcmasp_intf *intf = netdev_priv(dev);
166	struct bcmasp_priv *priv = intf->parent;
167	struct device *kdev = &priv->pdev->dev;
168	u32 phy_wolopts = 0;
169
170	if (dev->phydev) {
171		phy_ethtool_get_wol(dev->phydev, wol);
172		phy_wolopts = wol->wolopts;
173	}
174
175	/* MAC is not wake-up capable, return what the PHY does */
176	if (!device_can_wakeup(kdev))
177		return;
178
179	/* Overlay MAC capabilities with that of the PHY queried before */
180	wol->supported |= BCMASP_SUPPORTED_WAKE;
181	wol->wolopts |= intf->wolopts;
182
183	/* Return the PHY configured magic password */
184	if (phy_wolopts & WAKE_MAGICSECURE)
185		return;
186
187	memset(wol->sopass, 0, sizeof(wol->sopass));
188
189	/* Otherwise the MAC one */
190	if (wol->wolopts & WAKE_MAGICSECURE)
191		memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass));
192}
193
194static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
195{
196	struct bcmasp_intf *intf = netdev_priv(dev);
197	struct bcmasp_priv *priv = intf->parent;
198	struct device *kdev = &priv->pdev->dev;
199	int ret = 0;
200
201	/* Try Wake-on-LAN from the PHY first */
202	if (dev->phydev) {
203		ret = phy_ethtool_set_wol(dev->phydev, wol);
204		if (ret != -EOPNOTSUPP && wol->wolopts)
205			return ret;
206	}
207
208	if (!device_can_wakeup(kdev))
209		return -EOPNOTSUPP;
210
211	if (wol->wolopts & ~BCMASP_SUPPORTED_WAKE)
212		return -EINVAL;
213
214	/* Interface Specific */
215	intf->wolopts = wol->wolopts;
216	if (intf->wolopts & WAKE_MAGICSECURE)
217		memcpy(intf->sopass, wol->sopass, sizeof(wol->sopass));
218
219	mutex_lock(&priv->wol_lock);
220	bcmasp_enable_wol(intf, !!intf->wolopts);
221	mutex_unlock(&priv->wol_lock);
222
223	return 0;
224}
225
226static int bcmasp_flow_insert(struct net_device *dev, struct ethtool_rxnfc *cmd)
227{
228	struct bcmasp_intf *intf = netdev_priv(dev);
229	struct bcmasp_net_filter *nfilter;
230	u32 loc = cmd->fs.location;
231	bool wake = false;
232
233	if (cmd->fs.ring_cookie == RX_CLS_FLOW_WAKE)
234		wake = true;
235
236	/* Currently only supports WAKE filters */
237	if (!wake)
238		return -EOPNOTSUPP;
239
240	switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
241	case ETHER_FLOW:
242	case IP_USER_FLOW:
243	case TCP_V4_FLOW:
244	case UDP_V4_FLOW:
245	case TCP_V6_FLOW:
246	case UDP_V6_FLOW:
247		break;
248	default:
249		return -EOPNOTSUPP;
250	}
251
252	/* Check if filter already exists */
253	if (bcmasp_netfilt_check_dup(intf, &cmd->fs))
254		return -EINVAL;
255
256	nfilter = bcmasp_netfilt_get_init(intf, loc, wake, true);
257	if (IS_ERR(nfilter))
258		return PTR_ERR(nfilter);
259
260	/* Return the location where we did insert the filter */
261	cmd->fs.location = nfilter->hw_index;
262	memcpy(&nfilter->fs, &cmd->fs, sizeof(struct ethtool_rx_flow_spec));
263
264	/* Since we only support wake filters, defer register programming till
265	 * suspend time.
266	 */
267	return 0;
268}
269
270static int bcmasp_flow_delete(struct net_device *dev, struct ethtool_rxnfc *cmd)
271{
272	struct bcmasp_intf *intf = netdev_priv(dev);
273	struct bcmasp_net_filter *nfilter;
274
275	nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false);
276	if (IS_ERR(nfilter))
277		return PTR_ERR(nfilter);
278
279	bcmasp_netfilt_release(intf, nfilter);
280
281	return 0;
282}
283
284static int bcmasp_flow_get(struct bcmasp_intf *intf, struct ethtool_rxnfc *cmd)
285{
286	struct bcmasp_net_filter *nfilter;
287
288	nfilter = bcmasp_netfilt_get_init(intf, cmd->fs.location, false, false);
289	if (IS_ERR(nfilter))
290		return PTR_ERR(nfilter);
291
292	memcpy(&cmd->fs, &nfilter->fs, sizeof(nfilter->fs));
293
294	cmd->data = intf->parent->num_net_filters;
295
296	return 0;
297}
298
299static int bcmasp_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
300{
301	struct bcmasp_intf *intf = netdev_priv(dev);
302	int ret = -EOPNOTSUPP;
303
304	mutex_lock(&intf->parent->net_lock);
305
306	switch (cmd->cmd) {
307	case ETHTOOL_SRXCLSRLINS:
308		ret = bcmasp_flow_insert(dev, cmd);
309		break;
310	case ETHTOOL_SRXCLSRLDEL:
311		ret = bcmasp_flow_delete(dev, cmd);
312		break;
313	default:
314		break;
315	}
316
317	mutex_unlock(&intf->parent->net_lock);
318
319	return ret;
320}
321
322static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
323			    u32 *rule_locs)
324{
325	struct bcmasp_intf *intf = netdev_priv(dev);
326	int err = 0;
327
328	mutex_lock(&intf->parent->net_lock);
329
330	switch (cmd->cmd) {
331	case ETHTOOL_GRXCLSRLCNT:
332		cmd->rule_cnt = bcmasp_netfilt_get_active(intf);
333		/* We support specifying rule locations */
334		cmd->data |= RX_CLS_LOC_SPECIAL;
335		break;
336	case ETHTOOL_GRXCLSRULE:
337		err = bcmasp_flow_get(intf, cmd);
338		break;
339	case ETHTOOL_GRXCLSRLALL:
340		err = bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt);
341		cmd->data = intf->parent->num_net_filters;
342		break;
343	default:
344		err = -EOPNOTSUPP;
345		break;
346	}
347
348	mutex_unlock(&intf->parent->net_lock);
349
350	return err;
351}
352
353static int bcmasp_get_eee(struct net_device *dev, struct ethtool_keee *e)
354{
355	if (!dev->phydev)
356		return -ENODEV;
357
358	return phy_ethtool_get_eee(dev->phydev, e);
359}
360
361static int bcmasp_set_eee(struct net_device *dev, struct ethtool_keee *e)
362{
363	if (!dev->phydev)
364		return -ENODEV;
365
366	return phy_ethtool_set_eee(dev->phydev, e);
367}
368
369static void bcmasp_get_eth_mac_stats(struct net_device *dev,
370				     struct ethtool_eth_mac_stats *mac_stats)
371{
372	struct bcmasp_intf *intf = netdev_priv(dev);
373
374	mac_stats->FramesTransmittedOK = umac_rl(intf, UMC_GTPOK);
375	mac_stats->SingleCollisionFrames = umac_rl(intf, UMC_GTSCL);
376	mac_stats->MultipleCollisionFrames = umac_rl(intf, UMC_GTMCL);
377	mac_stats->FramesReceivedOK = umac_rl(intf, UMC_GRPOK);
378	mac_stats->FrameCheckSequenceErrors = umac_rl(intf, UMC_GRFCS);
379	mac_stats->AlignmentErrors = umac_rl(intf, UMC_GRALN);
380	mac_stats->OctetsTransmittedOK = umac_rl(intf, UMC_GTBYT);
381	mac_stats->FramesWithDeferredXmissions = umac_rl(intf, UMC_GTDRF);
382	mac_stats->LateCollisions = umac_rl(intf, UMC_GTLCL);
383	mac_stats->FramesAbortedDueToXSColls = umac_rl(intf, UMC_GTXCL);
384	mac_stats->OctetsReceivedOK = umac_rl(intf, UMC_GRBYT);
385	mac_stats->MulticastFramesXmittedOK = umac_rl(intf, UMC_GTMCA);
386	mac_stats->BroadcastFramesXmittedOK = umac_rl(intf, UMC_GTBCA);
387	mac_stats->FramesWithExcessiveDeferral = umac_rl(intf, UMC_GTEDF);
388	mac_stats->MulticastFramesReceivedOK = umac_rl(intf, UMC_GRMCA);
389	mac_stats->BroadcastFramesReceivedOK = umac_rl(intf, UMC_GRBCA);
390}
391
392static const struct ethtool_rmon_hist_range bcmasp_rmon_ranges[] = {
393	{    0,   64},
394	{   65,  127},
395	{  128,  255},
396	{  256,  511},
397	{  512, 1023},
398	{ 1024, 1518},
399	{ 1519, 1522},
400	{}
401};
402
403static void bcmasp_get_rmon_stats(struct net_device *dev,
404				  struct ethtool_rmon_stats *rmon_stats,
405				  const struct ethtool_rmon_hist_range **ranges)
406{
407	struct bcmasp_intf *intf = netdev_priv(dev);
408
409	*ranges = bcmasp_rmon_ranges;
410
411	rmon_stats->undersize_pkts = umac_rl(intf, UMC_RRUND);
412	rmon_stats->oversize_pkts = umac_rl(intf, UMC_GROVR);
413	rmon_stats->fragments = umac_rl(intf, UMC_RRFRG);
414	rmon_stats->jabbers = umac_rl(intf, UMC_GRJBR);
415
416	rmon_stats->hist[0] = umac_rl(intf, UMC_GR64);
417	rmon_stats->hist[1] = umac_rl(intf, UMC_GR127);
418	rmon_stats->hist[2] = umac_rl(intf, UMC_GR255);
419	rmon_stats->hist[3] = umac_rl(intf, UMC_GR511);
420	rmon_stats->hist[4] = umac_rl(intf, UMC_GR1023);
421	rmon_stats->hist[5] = umac_rl(intf, UMC_GR1518);
422	rmon_stats->hist[6] = umac_rl(intf, UMC_GRMGV);
423
424	rmon_stats->hist_tx[0] = umac_rl(intf, UMC_TR64);
425	rmon_stats->hist_tx[1] = umac_rl(intf, UMC_TR127);
426	rmon_stats->hist_tx[2] = umac_rl(intf, UMC_TR255);
427	rmon_stats->hist_tx[3] = umac_rl(intf, UMC_TR511);
428	rmon_stats->hist_tx[4] = umac_rl(intf, UMC_TR1023);
429	rmon_stats->hist_tx[5] = umac_rl(intf, UMC_TR1518);
430	rmon_stats->hist_tx[6] = umac_rl(intf, UMC_TRMGV);
431}
432
433static void bcmasp_get_eth_ctrl_stats(struct net_device *dev,
434				      struct ethtool_eth_ctrl_stats *ctrl_stats)
435{
436	struct bcmasp_intf *intf = netdev_priv(dev);
437
438	ctrl_stats->MACControlFramesTransmitted = umac_rl(intf, UMC_GTXCF);
439	ctrl_stats->MACControlFramesReceived = umac_rl(intf, UMC_GRXCF);
440	ctrl_stats->UnsupportedOpcodesReceived = umac_rl(intf, UMC_GRXUO);
441}
442
443const struct ethtool_ops bcmasp_ethtool_ops = {
444	.get_drvinfo		= bcmasp_get_drvinfo,
445	.get_link		= ethtool_op_get_link,
446	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
447	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
448	.get_msglevel		= bcmasp_get_msglevel,
449	.set_msglevel		= bcmasp_set_msglevel,
450	.get_wol		= bcmasp_get_wol,
451	.set_wol		= bcmasp_set_wol,
452	.get_rxnfc		= bcmasp_get_rxnfc,
453	.set_rxnfc		= bcmasp_set_rxnfc,
454	.set_eee		= bcmasp_set_eee,
455	.get_eee		= bcmasp_get_eee,
456	.get_eth_mac_stats	= bcmasp_get_eth_mac_stats,
457	.get_rmon_stats		= bcmasp_get_rmon_stats,
458	.get_eth_ctrl_stats	= bcmasp_get_eth_ctrl_stats,
459	.get_strings		= bcmasp_get_strings,
460	.get_ethtool_stats	= bcmasp_get_ethtool_stats,
461	.get_sset_count		= bcmasp_get_sset_count,
462	.get_ts_info		= ethtool_op_get_ts_info,
463	.nway_reset		= phy_ethtool_nway_reset,
464};
~/ Read-only file preview.