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