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