]>
Commit | Line | Data |
---|---|---|
7cbc1968 | 1 | // SPDX-License-Identifier: GPL-2.0 |
e3cbf478 JC |
2 | /* This program is free software; you can redistribute it and/or modify |
3 | * it under the terms of the GNU General Public License as published by | |
4 | * the Free Software Foundation; version 2 of the License | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org> | |
12 | * Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org> | |
13 | * Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com> | |
14 | */ | |
15 | ||
16 | #include "mtk_eth_soc.h" | |
37dc8f16 | 17 | #include "ethtool.h" |
e3cbf478 | 18 | |
d84c7b30 KH |
19 | struct mtk_stat { |
20 | char name[ETH_GSTRING_LEN]; | |
21 | unsigned int idx; | |
e3cbf478 JC |
22 | }; |
23 | ||
d84c7b30 KH |
24 | #define MTK_HW_STAT(stat) { \ |
25 | .name = #stat, \ | |
26 | .idx = offsetof(struct mtk_hw_stats, stat) / sizeof(u64) \ | |
27 | } | |
28 | ||
29 | static const struct mtk_stat mtk_ethtool_hw_stats[] = { | |
30 | MTK_HW_STAT(tx_bytes), | |
31 | MTK_HW_STAT(tx_packets), | |
32 | MTK_HW_STAT(tx_skip), | |
33 | MTK_HW_STAT(tx_collisions), | |
34 | MTK_HW_STAT(rx_bytes), | |
35 | MTK_HW_STAT(rx_packets), | |
36 | MTK_HW_STAT(rx_overflow), | |
37 | MTK_HW_STAT(rx_fcs_errors), | |
38 | MTK_HW_STAT(rx_short_errors), | |
39 | MTK_HW_STAT(rx_long_errors), | |
40 | MTK_HW_STAT(rx_checksum_errors), | |
41 | MTK_HW_STAT(rx_flow_control_packets), | |
42 | }; | |
43 | ||
44 | #define MTK_HW_STATS_LEN ARRAY_SIZE(mtk_ethtool_hw_stats) | |
45 | ||
e3cbf478 JC |
46 | static int mtk_get_link_ksettings(struct net_device *dev, |
47 | struct ethtool_link_ksettings *cmd) | |
48 | { | |
49 | struct mtk_mac *mac = netdev_priv(dev); | |
50 | int err; | |
51 | ||
52 | if (!mac->phy_dev) | |
53 | return -ENODEV; | |
54 | ||
55 | if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) { | |
56 | err = phy_read_status(mac->phy_dev); | |
57 | if (err) | |
58 | return -ENODEV; | |
59 | } | |
60 | ||
61 | phy_ethtool_ksettings_get(mac->phy_dev, cmd); | |
62 | return 0; | |
63 | } | |
64 | ||
65 | static int mtk_set_link_ksettings(struct net_device *dev, | |
66 | const struct ethtool_link_ksettings *cmd) | |
67 | { | |
68 | struct mtk_mac *mac = netdev_priv(dev); | |
69 | ||
70 | if (!mac->phy_dev) | |
71 | return -ENODEV; | |
72 | ||
73 | if (cmd->base.phy_address != mac->phy_dev->mdio.addr) { | |
74 | if (mac->hw->phy->phy_node[cmd->base.phy_address]) { | |
75 | mac->phy_dev = mac->hw->phy->phy[cmd->base.phy_address]; | |
76 | mac->phy_flags = MTK_PHY_FLAG_PORT; | |
77 | } else if (mac->hw->mii_bus) { | |
fcd90a6d KH |
78 | mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus, |
79 | cmd->base.phy_address); | |
e3cbf478 JC |
80 | if (!mac->phy_dev) |
81 | return -ENODEV; | |
82 | mac->phy_flags = MTK_PHY_FLAG_ATTACH; | |
83 | } else { | |
84 | return -ENODEV; | |
85 | } | |
86 | } | |
87 | ||
88 | return phy_ethtool_ksettings_set(mac->phy_dev, cmd); | |
e3cbf478 JC |
89 | } |
90 | ||
91 | static void mtk_get_drvinfo(struct net_device *dev, | |
92 | struct ethtool_drvinfo *info) | |
93 | { | |
94 | struct mtk_mac *mac = netdev_priv(dev); | |
95 | struct mtk_soc_data *soc = mac->hw->soc; | |
96 | ||
97 | strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver)); | |
98 | strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info)); | |
99 | ||
100 | if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) | |
d84c7b30 | 101 | info->n_stats = MTK_HW_STATS_LEN; |
e3cbf478 JC |
102 | } |
103 | ||
104 | static u32 mtk_get_msglevel(struct net_device *dev) | |
105 | { | |
106 | struct mtk_mac *mac = netdev_priv(dev); | |
107 | ||
108 | return mac->hw->msg_enable; | |
109 | } | |
110 | ||
111 | static void mtk_set_msglevel(struct net_device *dev, u32 value) | |
112 | { | |
113 | struct mtk_mac *mac = netdev_priv(dev); | |
114 | ||
115 | mac->hw->msg_enable = value; | |
116 | } | |
117 | ||
118 | static int mtk_nway_reset(struct net_device *dev) | |
119 | { | |
120 | struct mtk_mac *mac = netdev_priv(dev); | |
121 | ||
122 | if (!mac->phy_dev) | |
123 | return -EOPNOTSUPP; | |
124 | ||
125 | return genphy_restart_aneg(mac->phy_dev); | |
126 | } | |
127 | ||
128 | static u32 mtk_get_link(struct net_device *dev) | |
129 | { | |
130 | struct mtk_mac *mac = netdev_priv(dev); | |
131 | int err; | |
132 | ||
133 | if (!mac->phy_dev) | |
134 | goto out_get_link; | |
135 | ||
136 | if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) { | |
137 | err = genphy_update_link(mac->phy_dev); | |
138 | if (err) | |
139 | goto out_get_link; | |
140 | } | |
141 | ||
142 | return mac->phy_dev->link; | |
143 | ||
144 | out_get_link: | |
145 | return ethtool_op_get_link(dev); | |
146 | } | |
147 | ||
148 | static int mtk_set_ringparam(struct net_device *dev, | |
149 | struct ethtool_ringparam *ring) | |
150 | { | |
151 | struct mtk_mac *mac = netdev_priv(dev); | |
152 | ||
153 | if ((ring->tx_pending < 2) || | |
154 | (ring->rx_pending < 2) || | |
155 | (ring->rx_pending > mac->hw->soc->dma_ring_size) || | |
156 | (ring->tx_pending > mac->hw->soc->dma_ring_size)) | |
157 | return -EINVAL; | |
158 | ||
159 | dev->netdev_ops->ndo_stop(dev); | |
160 | ||
161 | mac->hw->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1); | |
162 | mac->hw->rx_ring[0].rx_ring_size = BIT(fls(ring->rx_pending) - 1); | |
163 | ||
164 | return dev->netdev_ops->ndo_open(dev); | |
165 | } | |
166 | ||
167 | static void mtk_get_ringparam(struct net_device *dev, | |
168 | struct ethtool_ringparam *ring) | |
169 | { | |
170 | struct mtk_mac *mac = netdev_priv(dev); | |
171 | ||
172 | ring->rx_max_pending = mac->hw->soc->dma_ring_size; | |
173 | ring->tx_max_pending = mac->hw->soc->dma_ring_size; | |
174 | ring->rx_pending = mac->hw->rx_ring[0].rx_ring_size; | |
175 | ring->tx_pending = mac->hw->tx_ring.tx_ring_size; | |
176 | } | |
177 | ||
178 | static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data) | |
179 | { | |
d84c7b30 KH |
180 | int i; |
181 | ||
e3cbf478 JC |
182 | switch (stringset) { |
183 | case ETH_SS_STATS: | |
d84c7b30 KH |
184 | for (i = 0; i < MTK_HW_STATS_LEN; i++) { |
185 | memcpy(data, mtk_ethtool_hw_stats[i].name, | |
186 | ETH_GSTRING_LEN); | |
187 | data += ETH_GSTRING_LEN; | |
188 | } | |
e3cbf478 JC |
189 | break; |
190 | } | |
191 | } | |
192 | ||
193 | static int mtk_get_sset_count(struct net_device *dev, int sset) | |
194 | { | |
195 | switch (sset) { | |
196 | case ETH_SS_STATS: | |
d84c7b30 | 197 | return MTK_HW_STATS_LEN; |
e3cbf478 JC |
198 | default: |
199 | return -EOPNOTSUPP; | |
200 | } | |
201 | } | |
202 | ||
203 | static void mtk_get_ethtool_stats(struct net_device *dev, | |
204 | struct ethtool_stats *stats, u64 *data) | |
205 | { | |
206 | struct mtk_mac *mac = netdev_priv(dev); | |
207 | struct mtk_hw_stats *hwstats = mac->hw_stats; | |
e3cbf478 JC |
208 | unsigned int start; |
209 | int i; | |
210 | ||
211 | if (netif_running(dev) && netif_device_present(dev)) { | |
212 | if (spin_trylock(&hwstats->stats_lock)) { | |
213 | mtk_stats_update_mac(mac); | |
214 | spin_unlock(&hwstats->stats_lock); | |
215 | } | |
216 | } | |
217 | ||
218 | do { | |
e3cbf478 | 219 | start = u64_stats_fetch_begin_irq(&hwstats->syncp); |
d84c7b30 KH |
220 | for (i = 0; i < MTK_HW_STATS_LEN; i++) |
221 | data[i] = ((u64 *)hwstats)[mtk_ethtool_hw_stats[i].idx]; | |
e3cbf478 JC |
222 | |
223 | } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start)); | |
224 | } | |
225 | ||
226 | static struct ethtool_ops mtk_ethtool_ops = { | |
227 | .get_link_ksettings = mtk_get_link_ksettings, | |
228 | .set_link_ksettings = mtk_set_link_ksettings, | |
229 | .get_drvinfo = mtk_get_drvinfo, | |
230 | .get_msglevel = mtk_get_msglevel, | |
231 | .set_msglevel = mtk_set_msglevel, | |
232 | .nway_reset = mtk_nway_reset, | |
233 | .get_link = mtk_get_link, | |
234 | .set_ringparam = mtk_set_ringparam, | |
235 | .get_ringparam = mtk_get_ringparam, | |
236 | }; | |
237 | ||
238 | void mtk_set_ethtool_ops(struct net_device *netdev) | |
239 | { | |
240 | struct mtk_mac *mac = netdev_priv(netdev); | |
241 | struct mtk_soc_data *soc = mac->hw->soc; | |
242 | ||
243 | if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) { | |
244 | mtk_ethtool_ops.get_strings = mtk_get_strings; | |
245 | mtk_ethtool_ops.get_sset_count = mtk_get_sset_count; | |
246 | mtk_ethtool_ops.get_ethtool_stats = mtk_get_ethtool_stats; | |
247 | } | |
248 | ||
249 | netdev->ethtool_ops = &mtk_ethtool_ops; | |
250 | } |