1 | /* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver |
2 | * |
3 | * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) |
4 | * |
5 | * This program is dual-licensed; you may select either version 2 of |
6 | * the GNU General Public License ("GPL") or BSD license ("BSD"). |
7 | * |
8 | * This Synopsys DWC XLGMAC software driver and associated documentation |
9 | * (hereinafter the "Software") is an unsupported proprietary work of |
10 | * Synopsys, Inc. unless otherwise expressly agreed to in writing between |
11 | * Synopsys and you. The Software IS NOT an item of Licensed Software or a |
12 | * Licensed Product under any End User Software License Agreement or |
13 | * Agreement for Licensed Products with Synopsys or any supplement thereto. |
14 | * Synopsys is a registered trademark of Synopsys, Inc. Other names included |
15 | * in the SOFTWARE may be the trademarks of their respective owners. |
16 | */ |
17 | |
18 | #include <linux/ethtool.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/netdevice.h> |
21 | |
22 | #include "dwc-xlgmac.h" |
23 | #include "dwc-xlgmac-reg.h" |
24 | |
25 | struct xlgmac_stats_desc { |
26 | char stat_string[ETH_GSTRING_LEN]; |
27 | int stat_offset; |
28 | }; |
29 | |
30 | #define XLGMAC_STAT(str, var) \ |
31 | { \ |
32 | str, \ |
33 | offsetof(struct xlgmac_pdata, stats.var), \ |
34 | } |
35 | |
36 | static const struct xlgmac_stats_desc xlgmac_gstring_stats[] = { |
37 | /* MMC TX counters */ |
38 | XLGMAC_STAT("tx_bytes" , txoctetcount_gb), |
39 | XLGMAC_STAT("tx_bytes_good" , txoctetcount_g), |
40 | XLGMAC_STAT("tx_packets" , txframecount_gb), |
41 | XLGMAC_STAT("tx_packets_good" , txframecount_g), |
42 | XLGMAC_STAT("tx_unicast_packets" , txunicastframes_gb), |
43 | XLGMAC_STAT("tx_broadcast_packets" , txbroadcastframes_gb), |
44 | XLGMAC_STAT("tx_broadcast_packets_good" , txbroadcastframes_g), |
45 | XLGMAC_STAT("tx_multicast_packets" , txmulticastframes_gb), |
46 | XLGMAC_STAT("tx_multicast_packets_good" , txmulticastframes_g), |
47 | XLGMAC_STAT("tx_vlan_packets_good" , txvlanframes_g), |
48 | XLGMAC_STAT("tx_64_byte_packets" , tx64octets_gb), |
49 | XLGMAC_STAT("tx_65_to_127_byte_packets" , tx65to127octets_gb), |
50 | XLGMAC_STAT("tx_128_to_255_byte_packets" , tx128to255octets_gb), |
51 | XLGMAC_STAT("tx_256_to_511_byte_packets" , tx256to511octets_gb), |
52 | XLGMAC_STAT("tx_512_to_1023_byte_packets" , tx512to1023octets_gb), |
53 | XLGMAC_STAT("tx_1024_to_max_byte_packets" , tx1024tomaxoctets_gb), |
54 | XLGMAC_STAT("tx_underflow_errors" , txunderflowerror), |
55 | XLGMAC_STAT("tx_pause_frames" , txpauseframes), |
56 | |
57 | /* MMC RX counters */ |
58 | XLGMAC_STAT("rx_bytes" , rxoctetcount_gb), |
59 | XLGMAC_STAT("rx_bytes_good" , rxoctetcount_g), |
60 | XLGMAC_STAT("rx_packets" , rxframecount_gb), |
61 | XLGMAC_STAT("rx_unicast_packets_good" , rxunicastframes_g), |
62 | XLGMAC_STAT("rx_broadcast_packets_good" , rxbroadcastframes_g), |
63 | XLGMAC_STAT("rx_multicast_packets_good" , rxmulticastframes_g), |
64 | XLGMAC_STAT("rx_vlan_packets" , rxvlanframes_gb), |
65 | XLGMAC_STAT("rx_64_byte_packets" , rx64octets_gb), |
66 | XLGMAC_STAT("rx_65_to_127_byte_packets" , rx65to127octets_gb), |
67 | XLGMAC_STAT("rx_128_to_255_byte_packets" , rx128to255octets_gb), |
68 | XLGMAC_STAT("rx_256_to_511_byte_packets" , rx256to511octets_gb), |
69 | XLGMAC_STAT("rx_512_to_1023_byte_packets" , rx512to1023octets_gb), |
70 | XLGMAC_STAT("rx_1024_to_max_byte_packets" , rx1024tomaxoctets_gb), |
71 | XLGMAC_STAT("rx_undersize_packets_good" , rxundersize_g), |
72 | XLGMAC_STAT("rx_oversize_packets_good" , rxoversize_g), |
73 | XLGMAC_STAT("rx_crc_errors" , rxcrcerror), |
74 | XLGMAC_STAT("rx_crc_errors_small_packets" , rxrunterror), |
75 | XLGMAC_STAT("rx_crc_errors_giant_packets" , rxjabbererror), |
76 | XLGMAC_STAT("rx_length_errors" , rxlengtherror), |
77 | XLGMAC_STAT("rx_out_of_range_errors" , rxoutofrangetype), |
78 | XLGMAC_STAT("rx_fifo_overflow_errors" , rxfifooverflow), |
79 | XLGMAC_STAT("rx_watchdog_errors" , rxwatchdogerror), |
80 | XLGMAC_STAT("rx_pause_frames" , rxpauseframes), |
81 | |
82 | /* Extra counters */ |
83 | XLGMAC_STAT("tx_tso_packets" , tx_tso_packets), |
84 | XLGMAC_STAT("rx_split_header_packets" , rx_split_header_packets), |
85 | XLGMAC_STAT("tx_process_stopped" , tx_process_stopped), |
86 | XLGMAC_STAT("rx_process_stopped" , rx_process_stopped), |
87 | XLGMAC_STAT("tx_buffer_unavailable" , tx_buffer_unavailable), |
88 | XLGMAC_STAT("rx_buffer_unavailable" , rx_buffer_unavailable), |
89 | XLGMAC_STAT("fatal_bus_error" , fatal_bus_error), |
90 | XLGMAC_STAT("tx_vlan_packets" , tx_vlan_packets), |
91 | XLGMAC_STAT("rx_vlan_packets" , rx_vlan_packets), |
92 | XLGMAC_STAT("napi_poll_isr" , napi_poll_isr), |
93 | XLGMAC_STAT("napi_poll_txtimer" , napi_poll_txtimer), |
94 | }; |
95 | |
96 | #define XLGMAC_STATS_COUNT ARRAY_SIZE(xlgmac_gstring_stats) |
97 | |
98 | static void xlgmac_ethtool_get_drvinfo(struct net_device *netdev, |
99 | struct ethtool_drvinfo *drvinfo) |
100 | { |
101 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
102 | u32 ver = pdata->hw_feat.version; |
103 | u32 snpsver, devid, userver; |
104 | |
105 | strscpy(p: drvinfo->driver, q: pdata->drv_name, size: sizeof(drvinfo->driver)); |
106 | strscpy(p: drvinfo->version, q: pdata->drv_ver, size: sizeof(drvinfo->version)); |
107 | strscpy(p: drvinfo->bus_info, q: dev_name(dev: pdata->dev), |
108 | size: sizeof(drvinfo->bus_info)); |
109 | /* S|SNPSVER: Synopsys-defined Version |
110 | * D|DEVID: Indicates the Device family |
111 | * U|USERVER: User-defined Version |
112 | */ |
113 | snpsver = XLGMAC_GET_REG_BITS(ver, MAC_VR_SNPSVER_POS, |
114 | MAC_VR_SNPSVER_LEN); |
115 | devid = XLGMAC_GET_REG_BITS(ver, MAC_VR_DEVID_POS, |
116 | MAC_VR_DEVID_LEN); |
117 | userver = XLGMAC_GET_REG_BITS(ver, MAC_VR_USERVER_POS, |
118 | MAC_VR_USERVER_LEN); |
119 | snprintf(buf: drvinfo->fw_version, size: sizeof(drvinfo->fw_version), |
120 | fmt: "S.D.U: %x.%x.%x" , snpsver, devid, userver); |
121 | } |
122 | |
123 | static u32 xlgmac_ethtool_get_msglevel(struct net_device *netdev) |
124 | { |
125 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
126 | |
127 | return pdata->msg_enable; |
128 | } |
129 | |
130 | static void xlgmac_ethtool_set_msglevel(struct net_device *netdev, |
131 | u32 msglevel) |
132 | { |
133 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
134 | |
135 | pdata->msg_enable = msglevel; |
136 | } |
137 | |
138 | static void xlgmac_ethtool_get_channels(struct net_device *netdev, |
139 | struct ethtool_channels *channel) |
140 | { |
141 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
142 | |
143 | channel->max_rx = XLGMAC_MAX_DMA_CHANNELS; |
144 | channel->max_tx = XLGMAC_MAX_DMA_CHANNELS; |
145 | channel->rx_count = pdata->rx_q_count; |
146 | channel->tx_count = pdata->tx_q_count; |
147 | } |
148 | |
149 | static int |
150 | xlgmac_ethtool_get_coalesce(struct net_device *netdev, |
151 | struct ethtool_coalesce *ec, |
152 | struct kernel_ethtool_coalesce *kernel_coal, |
153 | struct netlink_ext_ack *extack) |
154 | { |
155 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
156 | |
157 | ec->rx_coalesce_usecs = pdata->rx_usecs; |
158 | ec->rx_max_coalesced_frames = pdata->rx_frames; |
159 | ec->tx_max_coalesced_frames = pdata->tx_frames; |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static int |
165 | xlgmac_ethtool_set_coalesce(struct net_device *netdev, |
166 | struct ethtool_coalesce *ec, |
167 | struct kernel_ethtool_coalesce *kernel_coal, |
168 | struct netlink_ext_ack *extack) |
169 | { |
170 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
171 | struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; |
172 | unsigned int rx_frames, rx_riwt, rx_usecs; |
173 | unsigned int tx_frames; |
174 | |
175 | rx_usecs = ec->rx_coalesce_usecs; |
176 | rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs); |
177 | rx_frames = ec->rx_max_coalesced_frames; |
178 | tx_frames = ec->tx_max_coalesced_frames; |
179 | |
180 | if ((rx_riwt > XLGMAC_MAX_DMA_RIWT) || |
181 | (rx_riwt < XLGMAC_MIN_DMA_RIWT) || |
182 | (rx_frames > pdata->rx_desc_count)) |
183 | return -EINVAL; |
184 | |
185 | if (tx_frames > pdata->tx_desc_count) |
186 | return -EINVAL; |
187 | |
188 | pdata->rx_riwt = rx_riwt; |
189 | pdata->rx_usecs = rx_usecs; |
190 | pdata->rx_frames = rx_frames; |
191 | hw_ops->config_rx_coalesce(pdata); |
192 | |
193 | pdata->tx_frames = tx_frames; |
194 | hw_ops->config_tx_coalesce(pdata); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static void xlgmac_ethtool_get_strings(struct net_device *netdev, |
200 | u32 stringset, u8 *data) |
201 | { |
202 | int i; |
203 | |
204 | switch (stringset) { |
205 | case ETH_SS_STATS: |
206 | for (i = 0; i < XLGMAC_STATS_COUNT; i++) { |
207 | memcpy(data, xlgmac_gstring_stats[i].stat_string, |
208 | ETH_GSTRING_LEN); |
209 | data += ETH_GSTRING_LEN; |
210 | } |
211 | break; |
212 | default: |
213 | WARN_ON(1); |
214 | break; |
215 | } |
216 | } |
217 | |
218 | static int xlgmac_ethtool_get_sset_count(struct net_device *netdev, |
219 | int stringset) |
220 | { |
221 | int ret; |
222 | |
223 | switch (stringset) { |
224 | case ETH_SS_STATS: |
225 | ret = XLGMAC_STATS_COUNT; |
226 | break; |
227 | |
228 | default: |
229 | ret = -EOPNOTSUPP; |
230 | } |
231 | |
232 | return ret; |
233 | } |
234 | |
235 | static void xlgmac_ethtool_get_ethtool_stats(struct net_device *netdev, |
236 | struct ethtool_stats *stats, |
237 | u64 *data) |
238 | { |
239 | struct xlgmac_pdata *pdata = netdev_priv(dev: netdev); |
240 | u8 *stat; |
241 | int i; |
242 | |
243 | pdata->hw_ops.read_mmc_stats(pdata); |
244 | for (i = 0; i < XLGMAC_STATS_COUNT; i++) { |
245 | stat = (u8 *)pdata + xlgmac_gstring_stats[i].stat_offset; |
246 | *data++ = *(u64 *)stat; |
247 | } |
248 | } |
249 | |
250 | static const struct ethtool_ops xlgmac_ethtool_ops = { |
251 | .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | |
252 | ETHTOOL_COALESCE_MAX_FRAMES, |
253 | .get_drvinfo = xlgmac_ethtool_get_drvinfo, |
254 | .get_link = ethtool_op_get_link, |
255 | .get_msglevel = xlgmac_ethtool_get_msglevel, |
256 | .set_msglevel = xlgmac_ethtool_set_msglevel, |
257 | .get_channels = xlgmac_ethtool_get_channels, |
258 | .get_coalesce = xlgmac_ethtool_get_coalesce, |
259 | .set_coalesce = xlgmac_ethtool_set_coalesce, |
260 | .get_strings = xlgmac_ethtool_get_strings, |
261 | .get_sset_count = xlgmac_ethtool_get_sset_count, |
262 | .get_ethtool_stats = xlgmac_ethtool_get_ethtool_stats, |
263 | }; |
264 | |
265 | const struct ethtool_ops *xlgmac_get_ethtool_ops(void) |
266 | { |
267 | return &xlgmac_ethtool_ops; |
268 | } |
269 | |