ramips: add v4.9 support
[openwrt/openwrt.git] / target / linux / ramips / files-4.9 / 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_settings(struct net_device *dev,
24 struct ethtool_cmd *cmd)
25 {
26 struct fe_priv *priv = netdev_priv(dev);
27 int err;
28
29 if (!priv->phy_dev)
30 goto out_gset;
31
32 if (priv->phy_flags == FE_PHY_FLAG_ATTACH) {
33 err = phy_read_status(priv->phy_dev);
34 if (err)
35 goto out_gset;
36 }
37
38 return phy_ethtool_gset(priv->phy_dev, cmd);
39
40 out_gset:
41 return -ENODEV;
42 }
43
44 static int fe_set_settings(struct net_device *dev,
45 struct ethtool_cmd *cmd)
46 {
47 struct fe_priv *priv = netdev_priv(dev);
48
49 if (!priv->phy_dev)
50 goto out_sset;
51
52 if (cmd->phy_address != priv->phy_dev->mdio.addr) {
53 if (priv->phy->phy_node[cmd->phy_address]) {
54 priv->phy_dev = priv->phy->phy[cmd->phy_address];
55 priv->phy_flags = FE_PHY_FLAG_PORT;
56 } else if (priv->mii_bus && mdiobus_get_phy(priv->mii_bus, cmd->phy_address)) {
57 priv->phy_dev = mdiobus_get_phy(priv->mii_bus, cmd->phy_address);
58 priv->phy_flags = FE_PHY_FLAG_ATTACH;
59 } else {
60 goto out_sset;
61 }
62 }
63
64 return phy_ethtool_sset(priv->phy_dev, cmd);
65
66 out_sset:
67 return -ENODEV;
68 }
69
70 static void fe_get_drvinfo(struct net_device *dev,
71 struct ethtool_drvinfo *info)
72 {
73 struct fe_priv *priv = netdev_priv(dev);
74 struct fe_soc_data *soc = priv->soc;
75
76 strlcpy(info->driver, priv->device->driver->name, sizeof(info->driver));
77 strlcpy(info->version, MTK_FE_DRV_VERSION, sizeof(info->version));
78 strlcpy(info->bus_info, dev_name(priv->device), sizeof(info->bus_info));
79
80 if (soc->reg_table[FE_REG_FE_COUNTER_BASE])
81 info->n_stats = ARRAY_SIZE(fe_gdma_str);
82 }
83
84 static u32 fe_get_msglevel(struct net_device *dev)
85 {
86 struct fe_priv *priv = netdev_priv(dev);
87
88 return priv->msg_enable;
89 }
90
91 static void fe_set_msglevel(struct net_device *dev, u32 value)
92 {
93 struct fe_priv *priv = netdev_priv(dev);
94
95 priv->msg_enable = value;
96 }
97
98 static int fe_nway_reset(struct net_device *dev)
99 {
100 struct fe_priv *priv = netdev_priv(dev);
101
102 if (!priv->phy_dev)
103 goto out_nway_reset;
104
105 return genphy_restart_aneg(priv->phy_dev);
106
107 out_nway_reset:
108 return -EOPNOTSUPP;
109 }
110
111 static u32 fe_get_link(struct net_device *dev)
112 {
113 struct fe_priv *priv = netdev_priv(dev);
114 int err;
115
116 if (!priv->phy_dev)
117 goto out_get_link;
118
119 if (priv->phy_flags == FE_PHY_FLAG_ATTACH) {
120 err = genphy_update_link(priv->phy_dev);
121 if (err)
122 goto out_get_link;
123 }
124
125 return priv->phy_dev->link;
126
127 out_get_link:
128 return ethtool_op_get_link(dev);
129 }
130
131 static int fe_set_ringparam(struct net_device *dev,
132 struct ethtool_ringparam *ring)
133 {
134 struct fe_priv *priv = netdev_priv(dev);
135
136 if ((ring->tx_pending < 2) ||
137 (ring->rx_pending < 2) ||
138 (ring->rx_pending > MAX_DMA_DESC) ||
139 (ring->tx_pending > MAX_DMA_DESC))
140 return -EINVAL;
141
142 dev->netdev_ops->ndo_stop(dev);
143
144 priv->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
145 priv->rx_ring.rx_ring_size = BIT(fls(ring->rx_pending) - 1);
146
147 dev->netdev_ops->ndo_open(dev);
148
149 return 0;
150 }
151
152 static void fe_get_ringparam(struct net_device *dev,
153 struct ethtool_ringparam *ring)
154 {
155 struct fe_priv *priv = netdev_priv(dev);
156
157 ring->rx_max_pending = MAX_DMA_DESC;
158 ring->tx_max_pending = MAX_DMA_DESC;
159 ring->rx_pending = priv->rx_ring.rx_ring_size;
160 ring->tx_pending = priv->tx_ring.tx_ring_size;
161 }
162
163 static void fe_get_strings(struct net_device *dev, u32 stringset, u8 *data)
164 {
165 switch (stringset) {
166 case ETH_SS_STATS:
167 memcpy(data, *fe_gdma_str, sizeof(fe_gdma_str));
168 break;
169 }
170 }
171
172 static int fe_get_sset_count(struct net_device *dev, int sset)
173 {
174 switch (sset) {
175 case ETH_SS_STATS:
176 return ARRAY_SIZE(fe_gdma_str);
177 default:
178 return -EOPNOTSUPP;
179 }
180 }
181
182 static void fe_get_ethtool_stats(struct net_device *dev,
183 struct ethtool_stats *stats, u64 *data)
184 {
185 struct fe_priv *priv = netdev_priv(dev);
186 struct fe_hw_stats *hwstats = priv->hw_stats;
187 u64 *data_src, *data_dst;
188 unsigned int start;
189 int i;
190
191 if (netif_running(dev) && netif_device_present(dev)) {
192 if (spin_trylock(&hwstats->stats_lock)) {
193 fe_stats_update(priv);
194 spin_unlock(&hwstats->stats_lock);
195 }
196 }
197
198 do {
199 data_src = &hwstats->tx_bytes;
200 data_dst = data;
201 start = u64_stats_fetch_begin_irq(&hwstats->syncp);
202
203 for (i = 0; i < ARRAY_SIZE(fe_gdma_str); i++)
204 *data_dst++ = *data_src++;
205
206 } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
207 }
208
209 static struct ethtool_ops fe_ethtool_ops = {
210 .get_settings = fe_get_settings,
211 .set_settings = fe_set_settings,
212 .get_drvinfo = fe_get_drvinfo,
213 .get_msglevel = fe_get_msglevel,
214 .set_msglevel = fe_set_msglevel,
215 .nway_reset = fe_nway_reset,
216 .get_link = fe_get_link,
217 .set_ringparam = fe_set_ringparam,
218 .get_ringparam = fe_get_ringparam,
219 };
220
221 void fe_set_ethtool_ops(struct net_device *netdev)
222 {
223 struct fe_priv *priv = netdev_priv(netdev);
224 struct fe_soc_data *soc = priv->soc;
225
226 if (soc->reg_table[FE_REG_FE_COUNTER_BASE]) {
227 fe_ethtool_ops.get_strings = fe_get_strings;
228 fe_ethtool_ops.get_sset_count = fe_get_sset_count;
229 fe_ethtool_ops.get_ethtool_stats = fe_get_ethtool_stats;
230 }
231
232 netdev->ethtool_ops = &fe_ethtool_ops;
233 }