803346ac12a0ddb26829281350c554e5a7a91921
[openwrt/openwrt.git] / target / linux / ramips / files-4.14 / drivers / net / ethernet / mtk / ethtool.c
1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License as published by
3 * the Free Software Foundation; version 2 of the License
4 *
5 * This program is distributed in the hope that it will be useful,
6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8 * GNU General Public License for more details.
9 *
10 * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
11 * Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name>
12 * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
13 */
14
15 #include "mtk_eth_soc.h"
16
17 static const char fe_gdma_str[][ETH_GSTRING_LEN] = {
18 #define _FE(x...) # x,
19 FE_STAT_REG_DECLARE
20 #undef _FE
21 };
22
23 static int fe_get_link_ksettings(struct net_device *ndev,
24 struct ethtool_link_ksettings *cmd)
25 {
26 struct fe_priv *priv = netdev_priv(ndev);
27
28 if (!priv->phy_dev)
29 return -ENODEV;
30
31 if (priv->phy_flags == FE_PHY_FLAG_ATTACH) {
32 if (phy_read_status(priv->phy_dev))
33 return -ENODEV;
34 }
35
36 phy_ethtool_ksettings_get(ndev->phydev, cmd);
37
38 return 0;
39 }
40
41 static int fe_set_link_ksettings(struct net_device *ndev,
42 const struct ethtool_link_ksettings *cmd)
43 {
44 struct fe_priv *priv = netdev_priv(ndev);
45
46 if (!priv->phy_dev)
47 goto out_sset;
48
49 if (cmd->base.phy_address != priv->phy_dev->mdio.addr) {
50 if (priv->phy->phy_node[cmd->base.phy_address]) {
51 priv->phy_dev = priv->phy->phy[cmd->base.phy_address];
52 priv->phy_flags = FE_PHY_FLAG_PORT;
53 } else if (priv->mii_bus && mdiobus_get_phy(priv->mii_bus, cmd->base.phy_address)) {
54 priv->phy_dev = mdiobus_get_phy(priv->mii_bus, cmd->base.phy_address);
55 priv->phy_flags = FE_PHY_FLAG_ATTACH;
56 } else {
57 goto out_sset;
58 }
59 }
60
61 return phy_ethtool_ksettings_set(ndev->phydev, cmd);
62
63 out_sset:
64 return -ENODEV;
65 }
66
67 static void fe_get_drvinfo(struct net_device *dev,
68 struct ethtool_drvinfo *info)
69 {
70 struct fe_priv *priv = netdev_priv(dev);
71 struct fe_soc_data *soc = priv->soc;
72
73 strlcpy(info->driver, priv->device->driver->name, sizeof(info->driver));
74 strlcpy(info->version, MTK_FE_DRV_VERSION, sizeof(info->version));
75 strlcpy(info->bus_info, dev_name(priv->device), sizeof(info->bus_info));
76
77 if (soc->reg_table[FE_REG_FE_COUNTER_BASE])
78 info->n_stats = ARRAY_SIZE(fe_gdma_str);
79 }
80
81 static u32 fe_get_msglevel(struct net_device *dev)
82 {
83 struct fe_priv *priv = netdev_priv(dev);
84
85 return priv->msg_enable;
86 }
87
88 static void fe_set_msglevel(struct net_device *dev, u32 value)
89 {
90 struct fe_priv *priv = netdev_priv(dev);
91
92 priv->msg_enable = value;
93 }
94
95 static int fe_nway_reset(struct net_device *dev)
96 {
97 struct fe_priv *priv = netdev_priv(dev);
98
99 if (!priv->phy_dev)
100 goto out_nway_reset;
101
102 return genphy_restart_aneg(priv->phy_dev);
103
104 out_nway_reset:
105 return -EOPNOTSUPP;
106 }
107
108 static u32 fe_get_link(struct net_device *dev)
109 {
110 struct fe_priv *priv = netdev_priv(dev);
111 int err;
112
113 if (!priv->phy_dev)
114 goto out_get_link;
115
116 if (priv->phy_flags == FE_PHY_FLAG_ATTACH) {
117 err = genphy_update_link(priv->phy_dev);
118 if (err)
119 goto out_get_link;
120 }
121
122 return priv->phy_dev->link;
123
124 out_get_link:
125 return ethtool_op_get_link(dev);
126 }
127
128 static int fe_set_ringparam(struct net_device *dev,
129 struct ethtool_ringparam *ring)
130 {
131 struct fe_priv *priv = netdev_priv(dev);
132
133 if ((ring->tx_pending < 2) ||
134 (ring->rx_pending < 2) ||
135 (ring->rx_pending > MAX_DMA_DESC) ||
136 (ring->tx_pending > MAX_DMA_DESC))
137 return -EINVAL;
138
139 dev->netdev_ops->ndo_stop(dev);
140
141 priv->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
142 priv->rx_ring.rx_ring_size = BIT(fls(ring->rx_pending) - 1);
143
144 dev->netdev_ops->ndo_open(dev);
145
146 return 0;
147 }
148
149 static void fe_get_ringparam(struct net_device *dev,
150 struct ethtool_ringparam *ring)
151 {
152 struct fe_priv *priv = netdev_priv(dev);
153
154 ring->rx_max_pending = MAX_DMA_DESC;
155 ring->tx_max_pending = MAX_DMA_DESC;
156 ring->rx_pending = priv->rx_ring.rx_ring_size;
157 ring->tx_pending = priv->tx_ring.tx_ring_size;
158 }
159
160 static void fe_get_strings(struct net_device *dev, u32 stringset, u8 *data)
161 {
162 switch (stringset) {
163 case ETH_SS_STATS:
164 memcpy(data, *fe_gdma_str, sizeof(fe_gdma_str));
165 break;
166 }
167 }
168
169 static int fe_get_sset_count(struct net_device *dev, int sset)
170 {
171 switch (sset) {
172 case ETH_SS_STATS:
173 return ARRAY_SIZE(fe_gdma_str);
174 default:
175 return -EOPNOTSUPP;
176 }
177 }
178
179 static void fe_get_ethtool_stats(struct net_device *dev,
180 struct ethtool_stats *stats, u64 *data)
181 {
182 struct fe_priv *priv = netdev_priv(dev);
183 struct fe_hw_stats *hwstats = priv->hw_stats;
184 u64 *data_src, *data_dst;
185 unsigned int start;
186 int i;
187
188 if (netif_running(dev) && netif_device_present(dev)) {
189 if (spin_trylock(&hwstats->stats_lock)) {
190 fe_stats_update(priv);
191 spin_unlock(&hwstats->stats_lock);
192 }
193 }
194
195 do {
196 data_src = &hwstats->tx_bytes;
197 data_dst = data;
198 start = u64_stats_fetch_begin_irq(&hwstats->syncp);
199
200 for (i = 0; i < ARRAY_SIZE(fe_gdma_str); i++)
201 *data_dst++ = *data_src++;
202
203 } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
204 }
205
206 static struct ethtool_ops fe_ethtool_ops = {
207 .get_link_ksettings = fe_get_link_ksettings,
208 .set_link_ksettings = fe_set_link_ksettings,
209 .get_drvinfo = fe_get_drvinfo,
210 .get_msglevel = fe_get_msglevel,
211 .set_msglevel = fe_set_msglevel,
212 .nway_reset = fe_nway_reset,
213 .get_link = fe_get_link,
214 .set_ringparam = fe_set_ringparam,
215 .get_ringparam = fe_get_ringparam,
216 };
217
218 void fe_set_ethtool_ops(struct net_device *netdev)
219 {
220 struct fe_priv *priv = netdev_priv(netdev);
221 struct fe_soc_data *soc = priv->soc;
222
223 if (soc->reg_table[FE_REG_FE_COUNTER_BASE]) {
224 fe_ethtool_ops.get_strings = fe_get_strings;
225 fe_ethtool_ops.get_sset_count = fe_get_sset_count;
226 fe_ethtool_ops.get_ethtool_stats = fe_get_ethtool_stats;
227 }
228
229 netdev->ethtool_ops = &fe_ethtool_ops;
230 }