1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Texas Instruments ICSSG Ethernet driver |
3 | * |
4 | * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ |
5 | * |
6 | */ |
7 | |
8 | #include "icssg_prueth.h" |
9 | #include "icssg_stats.h" |
10 | |
11 | static void emac_get_drvinfo(struct net_device *ndev, |
12 | struct ethtool_drvinfo *info) |
13 | { |
14 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
15 | struct prueth *prueth = emac->prueth; |
16 | |
17 | strscpy(info->driver, dev_driver_string(prueth->dev), |
18 | sizeof(info->driver)); |
19 | strscpy(info->bus_info, dev_name(prueth->dev), sizeof(info->bus_info)); |
20 | } |
21 | |
22 | static u32 emac_get_msglevel(struct net_device *ndev) |
23 | { |
24 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
25 | |
26 | return emac->msg_enable; |
27 | } |
28 | |
29 | static void emac_set_msglevel(struct net_device *ndev, u32 value) |
30 | { |
31 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
32 | |
33 | emac->msg_enable = value; |
34 | } |
35 | |
36 | static int emac_get_link_ksettings(struct net_device *ndev, |
37 | struct ethtool_link_ksettings *ecmd) |
38 | { |
39 | return phy_ethtool_get_link_ksettings(ndev, cmd: ecmd); |
40 | } |
41 | |
42 | static int emac_set_link_ksettings(struct net_device *ndev, |
43 | const struct ethtool_link_ksettings *ecmd) |
44 | { |
45 | return phy_ethtool_set_link_ksettings(ndev, cmd: ecmd); |
46 | } |
47 | |
48 | static int emac_get_eee(struct net_device *ndev, struct ethtool_keee *edata) |
49 | { |
50 | if (!ndev->phydev) |
51 | return -EOPNOTSUPP; |
52 | |
53 | return phy_ethtool_get_eee(phydev: ndev->phydev, data: edata); |
54 | } |
55 | |
56 | static int emac_set_eee(struct net_device *ndev, struct ethtool_keee *edata) |
57 | { |
58 | if (!ndev->phydev) |
59 | return -EOPNOTSUPP; |
60 | |
61 | return phy_ethtool_set_eee(phydev: ndev->phydev, data: edata); |
62 | } |
63 | |
64 | static int emac_nway_reset(struct net_device *ndev) |
65 | { |
66 | return phy_ethtool_nway_reset(ndev); |
67 | } |
68 | |
69 | static int emac_get_sset_count(struct net_device *ndev, int stringset) |
70 | { |
71 | switch (stringset) { |
72 | case ETH_SS_STATS: |
73 | return ICSSG_NUM_ETHTOOL_STATS; |
74 | default: |
75 | return -EOPNOTSUPP; |
76 | } |
77 | } |
78 | |
79 | static void emac_get_strings(struct net_device *ndev, u32 stringset, u8 *data) |
80 | { |
81 | u8 *p = data; |
82 | int i; |
83 | |
84 | switch (stringset) { |
85 | case ETH_SS_STATS: |
86 | for (i = 0; i < ARRAY_SIZE(icssg_all_stats); i++) { |
87 | if (!icssg_all_stats[i].standard_stats) { |
88 | memcpy(p, icssg_all_stats[i].name, |
89 | ETH_GSTRING_LEN); |
90 | p += ETH_GSTRING_LEN; |
91 | } |
92 | } |
93 | break; |
94 | default: |
95 | break; |
96 | } |
97 | } |
98 | |
99 | static void emac_get_ethtool_stats(struct net_device *ndev, |
100 | struct ethtool_stats *stats, u64 *data) |
101 | { |
102 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
103 | int i; |
104 | |
105 | emac_update_hardware_stats(emac); |
106 | |
107 | for (i = 0; i < ARRAY_SIZE(icssg_all_stats); i++) |
108 | if (!icssg_all_stats[i].standard_stats) |
109 | *(data++) = emac->stats[i]; |
110 | } |
111 | |
112 | static int emac_get_ts_info(struct net_device *ndev, |
113 | struct ethtool_ts_info *info) |
114 | { |
115 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
116 | |
117 | info->so_timestamping = |
118 | SOF_TIMESTAMPING_TX_HARDWARE | |
119 | SOF_TIMESTAMPING_TX_SOFTWARE | |
120 | SOF_TIMESTAMPING_RX_HARDWARE | |
121 | SOF_TIMESTAMPING_RX_SOFTWARE | |
122 | SOF_TIMESTAMPING_SOFTWARE | |
123 | SOF_TIMESTAMPING_RAW_HARDWARE; |
124 | |
125 | info->phc_index = icss_iep_get_ptp_clock_idx(iep: emac->iep); |
126 | info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); |
127 | info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static int emac_set_channels(struct net_device *ndev, |
133 | struct ethtool_channels *ch) |
134 | { |
135 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
136 | |
137 | /* Check if interface is up. Can change the num queues when |
138 | * the interface is down. |
139 | */ |
140 | if (netif_running(dev: emac->ndev)) |
141 | return -EBUSY; |
142 | |
143 | emac->tx_ch_num = ch->tx_count; |
144 | |
145 | return 0; |
146 | } |
147 | |
148 | static void emac_get_channels(struct net_device *ndev, |
149 | struct ethtool_channels *ch) |
150 | { |
151 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
152 | |
153 | ch->max_rx = 1; |
154 | ch->max_tx = PRUETH_MAX_TX_QUEUES; |
155 | ch->rx_count = 1; |
156 | ch->tx_count = emac->tx_ch_num; |
157 | } |
158 | |
159 | static const struct ethtool_rmon_hist_range emac_rmon_ranges[] = { |
160 | { 0, 64}, |
161 | { 65, 128}, |
162 | { 129, 256}, |
163 | { 257, 512}, |
164 | { 513, PRUETH_MAX_PKT_SIZE}, |
165 | {} |
166 | }; |
167 | |
168 | static void emac_get_rmon_stats(struct net_device *ndev, |
169 | struct ethtool_rmon_stats *rmon_stats, |
170 | const struct ethtool_rmon_hist_range **ranges) |
171 | { |
172 | struct prueth_emac *emac = netdev_priv(dev: ndev); |
173 | |
174 | *ranges = emac_rmon_ranges; |
175 | |
176 | rmon_stats->undersize_pkts = emac_get_stat_by_name(emac, stat_name: "rx_bucket1_frames" ) - |
177 | emac_get_stat_by_name(emac, stat_name: "rx_64B_frames" ); |
178 | |
179 | rmon_stats->hist[0] = emac_get_stat_by_name(emac, stat_name: "rx_bucket1_frames" ); |
180 | rmon_stats->hist[1] = emac_get_stat_by_name(emac, stat_name: "rx_bucket2_frames" ); |
181 | rmon_stats->hist[2] = emac_get_stat_by_name(emac, stat_name: "rx_bucket3_frames" ); |
182 | rmon_stats->hist[3] = emac_get_stat_by_name(emac, stat_name: "rx_bucket4_frames" ); |
183 | rmon_stats->hist[4] = emac_get_stat_by_name(emac, stat_name: "rx_bucket5_frames" ); |
184 | |
185 | rmon_stats->hist_tx[0] = emac_get_stat_by_name(emac, stat_name: "tx_bucket1_frames" ); |
186 | rmon_stats->hist_tx[1] = emac_get_stat_by_name(emac, stat_name: "tx_bucket2_frames" ); |
187 | rmon_stats->hist_tx[2] = emac_get_stat_by_name(emac, stat_name: "tx_bucket3_frames" ); |
188 | rmon_stats->hist_tx[3] = emac_get_stat_by_name(emac, stat_name: "tx_bucket4_frames" ); |
189 | rmon_stats->hist_tx[4] = emac_get_stat_by_name(emac, stat_name: "tx_bucket5_frames" ); |
190 | } |
191 | |
192 | const struct ethtool_ops icssg_ethtool_ops = { |
193 | .get_drvinfo = emac_get_drvinfo, |
194 | .get_msglevel = emac_get_msglevel, |
195 | .set_msglevel = emac_set_msglevel, |
196 | .get_sset_count = emac_get_sset_count, |
197 | .get_ethtool_stats = emac_get_ethtool_stats, |
198 | .get_strings = emac_get_strings, |
199 | .get_ts_info = emac_get_ts_info, |
200 | .get_channels = emac_get_channels, |
201 | .set_channels = emac_set_channels, |
202 | .get_link_ksettings = emac_get_link_ksettings, |
203 | .set_link_ksettings = emac_set_link_ksettings, |
204 | .get_link = ethtool_op_get_link, |
205 | .get_eee = emac_get_eee, |
206 | .set_eee = emac_set_eee, |
207 | .nway_reset = emac_nway_reset, |
208 | .get_rmon_stats = emac_get_rmon_stats, |
209 | }; |
210 | |