1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * DPAA2 Ethernet Switch ethtool support
4  *
5  * Copyright 2014-2016 Freescale Semiconductor Inc.
6  * Copyright 2017-2018 NXP
7  *
8  */
9 
10 #include <linux/ethtool.h>
11 
12 #include "dpaa2-switch.h"
13 
14 static struct {
15 	enum dpsw_counter id;
16 	char name[ETH_GSTRING_LEN];
17 } dpaa2_switch_ethtool_counters[] =  {
18 	{DPSW_CNT_ING_FRAME,		"rx frames"},
19 	{DPSW_CNT_ING_BYTE,		"rx bytes"},
20 	{DPSW_CNT_ING_FLTR_FRAME,	"rx filtered frames"},
21 	{DPSW_CNT_ING_FRAME_DISCARD,	"rx discarded frames"},
22 	{DPSW_CNT_ING_BCAST_FRAME,	"rx b-cast frames"},
23 	{DPSW_CNT_ING_BCAST_BYTES,	"rx b-cast bytes"},
24 	{DPSW_CNT_ING_MCAST_FRAME,	"rx m-cast frames"},
25 	{DPSW_CNT_ING_MCAST_BYTE,	"rx m-cast bytes"},
26 	{DPSW_CNT_EGR_FRAME,		"tx frames"},
27 	{DPSW_CNT_EGR_BYTE,		"tx bytes"},
28 	{DPSW_CNT_EGR_FRAME_DISCARD,	"tx discarded frames"},
29 	{DPSW_CNT_ING_NO_BUFF_DISCARD,	"rx discarded no buffer frames"},
30 };
31 
32 #define DPAA2_SWITCH_NUM_COUNTERS	ARRAY_SIZE(dpaa2_switch_ethtool_counters)
33 
34 static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
35 				     struct ethtool_drvinfo *drvinfo)
36 {
37 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
38 	u16 version_major, version_minor;
39 	int err;
40 
41 	strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
42 
43 	err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
44 				   &version_major,
45 				   &version_minor);
46 	if (err)
47 		strscpy(drvinfo->fw_version, "N/A",
48 			sizeof(drvinfo->fw_version));
49 	else
50 		snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
51 			 "%u.%u", version_major, version_minor);
52 
53 	strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
54 		sizeof(drvinfo->bus_info));
55 }
56 
57 static int
58 dpaa2_switch_get_link_ksettings(struct net_device *netdev,
59 				struct ethtool_link_ksettings *link_ksettings)
60 {
61 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
62 	struct dpsw_link_state state = {0};
63 	int err = 0;
64 
65 	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
66 				     port_priv->ethsw_data->dpsw_handle,
67 				     port_priv->idx,
68 				     &state);
69 	if (err) {
70 		netdev_err(netdev, "ERROR %d getting link state\n", err);
71 		goto out;
72 	}
73 
74 	/* At the moment, we have no way of interrogating the DPMAC
75 	 * from the DPSW side or there may not exist a DPMAC at all.
76 	 * Report only autoneg state, duplexity and speed.
77 	 */
78 	if (state.options & DPSW_LINK_OPT_AUTONEG)
79 		link_ksettings->base.autoneg = AUTONEG_ENABLE;
80 	if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
81 		link_ksettings->base.duplex = DUPLEX_FULL;
82 	link_ksettings->base.speed = state.rate;
83 
84 out:
85 	return err;
86 }
87 
88 static int
89 dpaa2_switch_set_link_ksettings(struct net_device *netdev,
90 				const struct ethtool_link_ksettings *link_ksettings)
91 {
92 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
93 	struct ethsw_core *ethsw = port_priv->ethsw_data;
94 	struct dpsw_link_cfg cfg = {0};
95 	bool if_running;
96 	int err = 0, ret;
97 
98 	/* Interface needs to be down to change link settings */
99 	if_running = netif_running(netdev);
100 	if (if_running) {
101 		err = dpsw_if_disable(ethsw->mc_io, 0,
102 				      ethsw->dpsw_handle,
103 				      port_priv->idx);
104 		if (err) {
105 			netdev_err(netdev, "dpsw_if_disable err %d\n", err);
106 			return err;
107 		}
108 	}
109 
110 	cfg.rate = link_ksettings->base.speed;
111 	if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
112 		cfg.options |= DPSW_LINK_OPT_AUTONEG;
113 	else
114 		cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
115 	if (link_ksettings->base.duplex  == DUPLEX_HALF)
116 		cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
117 	else
118 		cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
119 
120 	err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
121 				   port_priv->ethsw_data->dpsw_handle,
122 				   port_priv->idx,
123 				   &cfg);
124 
125 	if (if_running) {
126 		ret = dpsw_if_enable(ethsw->mc_io, 0,
127 				     ethsw->dpsw_handle,
128 				     port_priv->idx);
129 		if (ret) {
130 			netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
131 			return ret;
132 		}
133 	}
134 	return err;
135 }
136 
137 static int dpaa2_switch_ethtool_get_sset_count(struct net_device *dev, int sset)
138 {
139 	switch (sset) {
140 	case ETH_SS_STATS:
141 		return DPAA2_SWITCH_NUM_COUNTERS;
142 	default:
143 		return -EOPNOTSUPP;
144 	}
145 }
146 
147 static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
148 					     u32 stringset, u8 *data)
149 {
150 	int i;
151 
152 	switch (stringset) {
153 	case ETH_SS_STATS:
154 		for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++)
155 			memcpy(data + i * ETH_GSTRING_LEN,
156 			       dpaa2_switch_ethtool_counters[i].name,
157 			       ETH_GSTRING_LEN);
158 		break;
159 	}
160 }
161 
162 static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
163 					   struct ethtool_stats *stats,
164 					   u64 *data)
165 {
166 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
167 	int i, err;
168 
169 	for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
170 		err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
171 					  port_priv->ethsw_data->dpsw_handle,
172 					  port_priv->idx,
173 					  dpaa2_switch_ethtool_counters[i].id,
174 					  &data[i]);
175 		if (err)
176 			netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
177 				   dpaa2_switch_ethtool_counters[i].name, err);
178 	}
179 }
180 
181 const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
182 	.get_drvinfo		= dpaa2_switch_get_drvinfo,
183 	.get_link		= ethtool_op_get_link,
184 	.get_link_ksettings	= dpaa2_switch_get_link_ksettings,
185 	.set_link_ksettings	= dpaa2_switch_set_link_ksettings,
186 	.get_strings		= dpaa2_switch_ethtool_get_strings,
187 	.get_ethtool_stats	= dpaa2_switch_ethtool_get_stats,
188 	.get_sset_count		= dpaa2_switch_ethtool_get_sset_count,
189 };
190