brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · 228a638 Raw
481 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */3 4#include "tsnep.h"5 6static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = {7	"rx_packets",8	"rx_bytes",9	"rx_dropped",10	"rx_multicast",11	"rx_alloc_failed",12	"rx_phy_errors",13	"rx_forwarded_phy_errors",14	"rx_invalid_frame_errors",15	"tx_packets",16	"tx_bytes",17	"tx_dropped",18};19 20struct tsnep_stats {21	u64 rx_packets;22	u64 rx_bytes;23	u64 rx_dropped;24	u64 rx_multicast;25	u64 rx_alloc_failed;26	u64 rx_phy_errors;27	u64 rx_forwarded_phy_errors;28	u64 rx_invalid_frame_errors;29	u64 tx_packets;30	u64 tx_bytes;31	u64 tx_dropped;32};33 34#define TSNEP_STATS_COUNT (sizeof(struct tsnep_stats) / sizeof(u64))35 36static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = {37	"rx_%d_packets",38	"rx_%d_bytes",39	"rx_%d_dropped",40	"rx_%d_multicast",41	"rx_%d_alloc_failed",42	"rx_%d_no_descriptor_errors",43	"rx_%d_buffer_too_small_errors",44	"rx_%d_fifo_overflow_errors",45	"rx_%d_invalid_frame_errors",46};47 48struct tsnep_rx_queue_stats {49	u64 rx_packets;50	u64 rx_bytes;51	u64 rx_dropped;52	u64 rx_multicast;53	u64 rx_alloc_failed;54	u64 rx_no_descriptor_errors;55	u64 rx_buffer_too_small_errors;56	u64 rx_fifo_overflow_errors;57	u64 rx_invalid_frame_errors;58};59 60#define TSNEP_RX_QUEUE_STATS_COUNT (sizeof(struct tsnep_rx_queue_stats) / \61				    sizeof(u64))62 63static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {64	"tx_%d_packets",65	"tx_%d_bytes",66	"tx_%d_dropped",67};68 69struct tsnep_tx_queue_stats {70	u64 tx_packets;71	u64 tx_bytes;72	u64 tx_dropped;73};74 75#define TSNEP_TX_QUEUE_STATS_COUNT (sizeof(struct tsnep_tx_queue_stats) / \76				    sizeof(u64))77 78static void tsnep_ethtool_get_drvinfo(struct net_device *netdev,79				      struct ethtool_drvinfo *drvinfo)80{81	struct tsnep_adapter *adapter = netdev_priv(netdev);82 83	strscpy(drvinfo->driver, TSNEP, sizeof(drvinfo->driver));84	strscpy(drvinfo->bus_info, dev_name(&adapter->pdev->dev),85		sizeof(drvinfo->bus_info));86}87 88static int tsnep_ethtool_get_regs_len(struct net_device *netdev)89{90	struct tsnep_adapter *adapter = netdev_priv(netdev);91	int len;92	int num_additional_queues;93 94	len = TSNEP_MAC_SIZE;95 96	/* first queue pair is within TSNEP_MAC_SIZE, only queues additional to97	 * the first queue pair extend the register length by TSNEP_QUEUE_SIZE98	 */99	num_additional_queues =100		max(adapter->num_tx_queues, adapter->num_rx_queues) - 1;101	len += TSNEP_QUEUE_SIZE * num_additional_queues;102 103	return len;104}105 106static void tsnep_ethtool_get_regs(struct net_device *netdev,107				   struct ethtool_regs *regs,108				   void *p)109{110	struct tsnep_adapter *adapter = netdev_priv(netdev);111 112	regs->version = 1;113 114	memcpy_fromio(p, adapter->addr, regs->len);115}116 117static u32 tsnep_ethtool_get_msglevel(struct net_device *netdev)118{119	struct tsnep_adapter *adapter = netdev_priv(netdev);120 121	return adapter->msg_enable;122}123 124static void tsnep_ethtool_set_msglevel(struct net_device *netdev, u32 data)125{126	struct tsnep_adapter *adapter = netdev_priv(netdev);127 128	adapter->msg_enable = data;129}130 131static void tsnep_ethtool_get_strings(struct net_device *netdev, u32 stringset,132				      u8 *data)133{134	struct tsnep_adapter *adapter = netdev_priv(netdev);135	int rx_count = adapter->num_rx_queues;136	int tx_count = adapter->num_tx_queues;137	int i, j;138 139	switch (stringset) {140	case ETH_SS_STATS:141		memcpy(data, tsnep_stats_strings, sizeof(tsnep_stats_strings));142		data += sizeof(tsnep_stats_strings);143 144		for (i = 0; i < rx_count; i++) {145			for (j = 0; j < TSNEP_RX_QUEUE_STATS_COUNT; j++) {146				snprintf(data, ETH_GSTRING_LEN,147					 tsnep_rx_queue_stats_strings[j], i);148				data += ETH_GSTRING_LEN;149			}150		}151 152		for (i = 0; i < tx_count; i++) {153			for (j = 0; j < TSNEP_TX_QUEUE_STATS_COUNT; j++) {154				snprintf(data, ETH_GSTRING_LEN,155					 tsnep_tx_queue_stats_strings[j], i);156				data += ETH_GSTRING_LEN;157			}158		}159		break;160	case ETH_SS_TEST:161		tsnep_ethtool_get_test_strings(data);162		break;163	}164}165 166static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,167					    struct ethtool_stats *stats,168					    u64 *data)169{170	struct tsnep_adapter *adapter = netdev_priv(netdev);171	int rx_count = adapter->num_rx_queues;172	int tx_count = adapter->num_tx_queues;173	struct tsnep_stats tsnep_stats;174	struct tsnep_rx_queue_stats tsnep_rx_queue_stats;175	struct tsnep_tx_queue_stats tsnep_tx_queue_stats;176	u32 reg;177	int i;178 179	memset(&tsnep_stats, 0, sizeof(tsnep_stats));180	for (i = 0; i < adapter->num_rx_queues; i++) {181		tsnep_stats.rx_packets += adapter->rx[i].packets;182		tsnep_stats.rx_bytes += adapter->rx[i].bytes;183		tsnep_stats.rx_dropped += adapter->rx[i].dropped;184		tsnep_stats.rx_multicast += adapter->rx[i].multicast;185		tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed;186	}187	reg = ioread32(adapter->addr + ECM_STAT);188	tsnep_stats.rx_phy_errors =189		(reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;190	tsnep_stats.rx_forwarded_phy_errors =191		(reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;192	tsnep_stats.rx_invalid_frame_errors =193		(reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;194	for (i = 0; i < adapter->num_tx_queues; i++) {195		tsnep_stats.tx_packets += adapter->tx[i].packets;196		tsnep_stats.tx_bytes += adapter->tx[i].bytes;197		tsnep_stats.tx_dropped += adapter->tx[i].dropped;198	}199	memcpy(data, &tsnep_stats, sizeof(tsnep_stats));200	data += TSNEP_STATS_COUNT;201 202	for (i = 0; i < rx_count; i++) {203		memset(&tsnep_rx_queue_stats, 0, sizeof(tsnep_rx_queue_stats));204		tsnep_rx_queue_stats.rx_packets = adapter->rx[i].packets;205		tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes;206		tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped;207		tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast;208		tsnep_rx_queue_stats.rx_alloc_failed =209			adapter->rx[i].alloc_failed;210		reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +211			       TSNEP_RX_STATISTIC);212		tsnep_rx_queue_stats.rx_no_descriptor_errors =213			(reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>214			TSNEP_RX_STATISTIC_NO_DESC_SHIFT;215		tsnep_rx_queue_stats.rx_buffer_too_small_errors =216			(reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>217			TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;218		tsnep_rx_queue_stats.rx_fifo_overflow_errors =219			(reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>220			TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;221		tsnep_rx_queue_stats.rx_invalid_frame_errors =222			(reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>223			TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;224		memcpy(data, &tsnep_rx_queue_stats,225		       sizeof(tsnep_rx_queue_stats));226		data += TSNEP_RX_QUEUE_STATS_COUNT;227	}228 229	for (i = 0; i < tx_count; i++) {230		memset(&tsnep_tx_queue_stats, 0, sizeof(tsnep_tx_queue_stats));231		tsnep_tx_queue_stats.tx_packets += adapter->tx[i].packets;232		tsnep_tx_queue_stats.tx_bytes += adapter->tx[i].bytes;233		tsnep_tx_queue_stats.tx_dropped += adapter->tx[i].dropped;234		memcpy(data, &tsnep_tx_queue_stats,235		       sizeof(tsnep_tx_queue_stats));236		data += TSNEP_TX_QUEUE_STATS_COUNT;237	}238}239 240static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset)241{242	struct tsnep_adapter *adapter = netdev_priv(netdev);243	int rx_count;244	int tx_count;245 246	switch (sset) {247	case ETH_SS_STATS:248		rx_count = adapter->num_rx_queues;249		tx_count = adapter->num_tx_queues;250		return TSNEP_STATS_COUNT +251		       TSNEP_RX_QUEUE_STATS_COUNT * rx_count +252		       TSNEP_TX_QUEUE_STATS_COUNT * tx_count;253	case ETH_SS_TEST:254		return tsnep_ethtool_get_test_count();255	default:256		return -EOPNOTSUPP;257	}258}259 260static int tsnep_ethtool_get_rxnfc(struct net_device *netdev,261				   struct ethtool_rxnfc *cmd, u32 *rule_locs)262{263	struct tsnep_adapter *adapter = netdev_priv(netdev);264 265	switch (cmd->cmd) {266	case ETHTOOL_GRXRINGS:267		cmd->data = adapter->num_rx_queues;268		return 0;269	case ETHTOOL_GRXCLSRLCNT:270		cmd->rule_cnt = adapter->rxnfc_count;271		cmd->data = adapter->rxnfc_max;272		cmd->data |= RX_CLS_LOC_SPECIAL;273		return 0;274	case ETHTOOL_GRXCLSRULE:275		return tsnep_rxnfc_get_rule(adapter, cmd);276	case ETHTOOL_GRXCLSRLALL:277		return tsnep_rxnfc_get_all(adapter, cmd, rule_locs);278	default:279		return -EOPNOTSUPP;280	}281}282 283static int tsnep_ethtool_set_rxnfc(struct net_device *netdev,284				   struct ethtool_rxnfc *cmd)285{286	struct tsnep_adapter *adapter = netdev_priv(netdev);287 288	switch (cmd->cmd) {289	case ETHTOOL_SRXCLSRLINS:290		return tsnep_rxnfc_add_rule(adapter, cmd);291	case ETHTOOL_SRXCLSRLDEL:292		return tsnep_rxnfc_del_rule(adapter, cmd);293	default:294		return -EOPNOTSUPP;295	}296}297 298static void tsnep_ethtool_get_channels(struct net_device *netdev,299				       struct ethtool_channels *ch)300{301	struct tsnep_adapter *adapter = netdev_priv(netdev);302 303	ch->max_combined = adapter->num_queues;304	ch->combined_count = adapter->num_queues;305}306 307static int tsnep_ethtool_get_ts_info(struct net_device *netdev,308				     struct kernel_ethtool_ts_info *info)309{310	struct tsnep_adapter *adapter = netdev_priv(netdev);311 312	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |313				SOF_TIMESTAMPING_TX_HARDWARE |314				SOF_TIMESTAMPING_RX_HARDWARE |315				SOF_TIMESTAMPING_RAW_HARDWARE;316 317	if (adapter->ptp_clock)318		info->phc_index = ptp_clock_index(adapter->ptp_clock);319 320	info->tx_types = BIT(HWTSTAMP_TX_OFF) |321			 BIT(HWTSTAMP_TX_ON);322	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |323			   BIT(HWTSTAMP_FILTER_ALL);324 325	return 0;326}327 328static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,329						   int index)330{331	int i;332 333	for (i = 0; i < adapter->num_queues; i++) {334		if (adapter->queue[i].tx) {335			if (index == 0)336				return &adapter->queue[i];337 338			index--;339		}340	}341 342	return NULL;343}344 345static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,346						   int index)347{348	int i;349 350	for (i = 0; i < adapter->num_queues; i++) {351		if (adapter->queue[i].rx) {352			if (index == 0)353				return &adapter->queue[i];354 355			index--;356		}357	}358 359	return NULL;360}361 362static int tsnep_ethtool_get_coalesce(struct net_device *netdev,363				      struct ethtool_coalesce *ec,364				      struct kernel_ethtool_coalesce *kernel_coal,365				      struct netlink_ext_ack *extack)366{367	struct tsnep_adapter *adapter = netdev_priv(netdev);368	struct tsnep_queue *queue;369 370	queue = tsnep_get_queue_with_rx(adapter, 0);371	if (queue)372		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);373 374	queue = tsnep_get_queue_with_tx(adapter, 0);375	if (queue)376		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);377 378	return 0;379}380 381static int tsnep_ethtool_set_coalesce(struct net_device *netdev,382				      struct ethtool_coalesce *ec,383				      struct kernel_ethtool_coalesce *kernel_coal,384				      struct netlink_ext_ack *extack)385{386	struct tsnep_adapter *adapter = netdev_priv(netdev);387	int i;388	int retval;389 390	for (i = 0; i < adapter->num_queues; i++) {391		/* RX coalesce has priority for queues with TX and RX */392		if (adapter->queue[i].rx)393			retval = tsnep_set_irq_coalesce(&adapter->queue[i],394							ec->rx_coalesce_usecs);395		else396			retval = tsnep_set_irq_coalesce(&adapter->queue[i],397							ec->tx_coalesce_usecs);398		if (retval != 0)399			return retval;400	}401 402	return 0;403}404 405static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,406						u32 queue,407						struct ethtool_coalesce *ec)408{409	struct tsnep_adapter *adapter = netdev_priv(netdev);410	struct tsnep_queue *queue_with_rx;411	struct tsnep_queue *queue_with_tx;412 413	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))414		return -EINVAL;415 416	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);417	if (queue_with_rx)418		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);419 420	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);421	if (queue_with_tx)422		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);423 424	return 0;425}426 427static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,428						u32 queue,429						struct ethtool_coalesce *ec)430{431	struct tsnep_adapter *adapter = netdev_priv(netdev);432	struct tsnep_queue *queue_with_rx;433	struct tsnep_queue *queue_with_tx;434	int retval;435 436	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))437		return -EINVAL;438 439	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);440	if (queue_with_rx) {441		retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);442		if (retval != 0)443			return retval;444	}445 446	/* RX coalesce has priority for queues with TX and RX */447	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);448	if (queue_with_tx && !queue_with_tx->rx) {449		retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);450		if (retval != 0)451			return retval;452	}453 454	return 0;455}456 457const struct ethtool_ops tsnep_ethtool_ops = {458	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,459	.get_drvinfo = tsnep_ethtool_get_drvinfo,460	.get_regs_len = tsnep_ethtool_get_regs_len,461	.get_regs = tsnep_ethtool_get_regs,462	.get_msglevel = tsnep_ethtool_get_msglevel,463	.set_msglevel = tsnep_ethtool_set_msglevel,464	.nway_reset = phy_ethtool_nway_reset,465	.get_link = ethtool_op_get_link,466	.self_test = tsnep_ethtool_self_test,467	.get_strings = tsnep_ethtool_get_strings,468	.get_ethtool_stats = tsnep_ethtool_get_ethtool_stats,469	.get_sset_count = tsnep_ethtool_get_sset_count,470	.get_rxnfc = tsnep_ethtool_get_rxnfc,471	.set_rxnfc = tsnep_ethtool_set_rxnfc,472	.get_channels = tsnep_ethtool_get_channels,473	.get_ts_info = tsnep_ethtool_get_ts_info,474	.get_coalesce = tsnep_ethtool_get_coalesce,475	.set_coalesce = tsnep_ethtool_set_coalesce,476	.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,477	.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,478	.get_link_ksettings = phy_ethtool_get_link_ksettings,479	.set_link_ksettings = phy_ethtool_set_link_ksettings,480};481