ipq40xx: add ipqess ethernet driver
[openwrt/staging/chunkeey.git] / target / linux / ipq40xx / files / drivers / net / ethernet / qualcomm / ipqess_ethtool.c
1 // SPDX-License-Identifier: (GPL-2.0 OR ISC)
2 /* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
3 * Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <linux/ethtool.h>
18 #include <linux/netdevice.h>
19 #include <linux/string.h>
20 #include <linux/phy.h>
21 #include "ipqess.h"
22
23 struct ipqesstool_stats {
24 uint8_t string[ETH_GSTRING_LEN];
25 uint32_t offset;
26 };
27
28 #define IPQESS_STAT(m) offsetof(struct ipqesstool_statistics, m)
29 #define DRVINFO_LEN 32
30
31 static const struct ipqesstool_stats ipqess_stats[] = {
32 {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
33 {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
34 {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
35 {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
36 {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
37 {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
38 {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
39 {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
40 {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
41 {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
42 {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
43 {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
44 {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
45 {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
46 {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
47 {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
48 {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
49 {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
50 {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
51 {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
52 {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
53 {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
54 {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
55 {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
56 {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
57 {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
58 {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
59 {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
60 {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
61 {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
62 {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
63 {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
64 {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
65 {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
66 {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
67 {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
68 {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
69 {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
70 {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
71 {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
72 {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
73 {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
74 {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
75 {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
76 {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
77 {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
78 {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
79 {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
80 {"tx_desc_error", IPQESS_STAT(tx_desc_error)},
81 };
82
83 static int ipqess_get_strset_count(struct net_device *netdev, int sset)
84 {
85 switch (sset) {
86 case ETH_SS_STATS:
87 return ARRAY_SIZE(ipqess_stats);
88 default:
89 netdev_dbg(netdev, "%s: Invalid string set", __func__);
90 return -EOPNOTSUPP;
91 }
92 }
93
94 static void ipqess_get_strings(struct net_device *netdev, uint32_t stringset,
95 uint8_t *data)
96 {
97 uint8_t *p = data;
98 uint32_t i;
99
100 switch (stringset) {
101 case ETH_SS_STATS:
102 for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) {
103 memcpy(p, ipqess_stats[i].string,
104 min((size_t)ETH_GSTRING_LEN,
105 strlen(ipqess_stats[i].string) + 1));
106 p += ETH_GSTRING_LEN;
107 }
108 break;
109 }
110 }
111
112 static void ipqess_get_drvinfo(struct net_device *dev,
113 struct ethtool_drvinfo *info)
114 {
115 strlcpy(info->driver, "qca_ipqess", DRVINFO_LEN);
116 strlcpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
117 }
118
119 static int ipqess_get_settings(struct net_device *netdev,
120 struct ethtool_cmd *ecmd)
121 {
122 struct phy_device *phydev = NULL;
123 uint16_t phyreg;
124
125 phydev = netdev->phydev;
126
127 ecmd->advertising = phydev->advertising;
128 ecmd->autoneg = phydev->autoneg;
129 ecmd->speed = phydev->speed;
130 ecmd->duplex = phydev->duplex;
131 ecmd->phy_address = phydev->mdio.addr;
132
133 phyreg = (uint16_t)phy_read(netdev->phydev, MII_LPA);
134 if (phyreg & LPA_10HALF)
135 ecmd->lp_advertising |= ADVERTISED_10baseT_Half;
136
137 if (phyreg & LPA_10FULL)
138 ecmd->lp_advertising |= ADVERTISED_10baseT_Full;
139
140 if (phyreg & LPA_100HALF)
141 ecmd->lp_advertising |= ADVERTISED_100baseT_Half;
142
143 if (phyreg & LPA_100FULL)
144 ecmd->lp_advertising |= ADVERTISED_100baseT_Full;
145
146 phyreg = (uint16_t)phy_read(netdev->phydev, MII_STAT1000);
147 if (phyreg & LPA_1000HALF)
148 ecmd->lp_advertising |= ADVERTISED_1000baseT_Half;
149
150 if (phyreg & LPA_1000FULL)
151 ecmd->lp_advertising |= ADVERTISED_1000baseT_Full;
152
153 return 0;
154 }
155
156 static int ipqess_set_settings(struct net_device *netdev,
157 struct ethtool_cmd *ecmd)
158 {
159 struct phy_device *phydev = NULL;
160
161 phydev = netdev->phydev;
162 phydev->advertising = ecmd->advertising;
163 phydev->autoneg = ecmd->autoneg;
164 phydev->speed = ethtool_cmd_speed(ecmd);
165 phydev->duplex = ecmd->duplex;
166
167 genphy_config_aneg(phydev);
168
169 return 0;
170 }
171
172 static void ipqess_get_ringparam(struct net_device *netdev,
173 struct ethtool_ringparam *ring)
174 {
175 ring->tx_max_pending = IPQESS_TX_RING_SIZE;
176 ring->rx_max_pending = IPQESS_RX_RING_SIZE;
177 }
178
179 static const struct ethtool_ops ipqesstool_ops = {
180 .get_drvinfo = &ipqess_get_drvinfo,
181 .get_link = &ethtool_op_get_link,
182 .get_settings = &ipqess_get_settings,
183 .set_settings = &ipqess_set_settings,
184 .get_strings = &ipqess_get_strings,
185 .get_sset_count = &ipqess_get_strset_count,
186 .get_ringparam = ipqess_get_ringparam,
187 };
188
189 void ipqess_set_ethtool_ops(struct net_device *netdev)
190 {
191 netdev->ethtool_ops = &ipqesstool_ops;
192 }