GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / net / ethernet / mscc / ocelot_mm.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /*
3  * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
4  * switches (VSC9959)
5  *
6  * Copyright 2022-2023 NXP
7  */
8 #include <linux/ethtool.h>
9 #include <soc/mscc/ocelot.h>
10 #include <soc/mscc/ocelot_dev.h>
11 #include <soc/mscc/ocelot_qsys.h>
12
13 #include "ocelot.h"
14
15 static const char *
16 mm_verify_state_to_string(enum ethtool_mm_verify_status state)
17 {
18         switch (state) {
19         case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
20                 return "INITIAL";
21         case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
22                 return "VERIFYING";
23         case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
24                 return "SUCCEEDED";
25         case ETHTOOL_MM_VERIFY_STATUS_FAILED:
26                 return "FAILED";
27         case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
28                 return "DISABLED";
29         default:
30                 return "UNKNOWN";
31         }
32 }
33
34 static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
35 {
36         switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
37         case 0:
38                 return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
39         case 1:
40                 return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
41         case 2:
42                 return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
43         case 3:
44                 return ETHTOOL_MM_VERIFY_STATUS_FAILED;
45         case 4:
46                 return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
47         default:
48                 return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
49         }
50 }
51
52 void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port)
53 {
54         struct ocelot_port *ocelot_port = ocelot->ports[port];
55         struct ocelot_mm_state *mm = &ocelot->mm[port];
56         u32 val = 0;
57
58         lockdep_assert_held(&ocelot->fwd_domain_lock);
59
60         /* Only commit preemptible TCs when MAC Merge is active.
61          * On NXP LS1028A, when using QSGMII, the port hangs if transmitting
62          * preemptible frames at any other link speed than gigabit, so avoid
63          * preemption at lower speeds in this PHY mode.
64          */
65         if ((ocelot_port->phy_mode != PHY_INTERFACE_MODE_QSGMII ||
66              ocelot_port->speed == SPEED_1000) && mm->tx_active)
67                 val = mm->preemptible_tcs;
68
69         /* Cut through switching doesn't work for preemptible priorities,
70          * so first make sure it is disabled. Also, changing the preemptible
71          * TCs affects the oversized frame dropping logic, so that needs to be
72          * re-triggered. And since tas_guard_bands_update() also implicitly
73          * calls cut_through_fwd(), we don't need to explicitly call it.
74          */
75         mm->active_preemptible_tcs = val;
76         ocelot->ops->tas_guard_bands_update(ocelot, port);
77
78         dev_dbg(ocelot->dev,
79                 "port %d %s/%s, MM TX %s, preemptible TCs 0x%x, active 0x%x\n",
80                 port, phy_modes(ocelot_port->phy_mode),
81                 phy_speed_to_str(ocelot_port->speed),
82                 mm->tx_active ? "active" : "inactive", mm->preemptible_tcs,
83                 mm->active_preemptible_tcs);
84
85         ocelot_rmw_rix(ocelot, QSYS_PREEMPTION_CFG_P_QUEUES(val),
86                        QSYS_PREEMPTION_CFG_P_QUEUES_M,
87                        QSYS_PREEMPTION_CFG, port);
88 }
89
90 void ocelot_port_change_fp(struct ocelot *ocelot, int port,
91                            unsigned long preemptible_tcs)
92 {
93         struct ocelot_mm_state *mm = &ocelot->mm[port];
94
95         lockdep_assert_held(&ocelot->fwd_domain_lock);
96
97         if (mm->preemptible_tcs == preemptible_tcs)
98                 return;
99
100         mm->preemptible_tcs = preemptible_tcs;
101
102         ocelot_port_update_active_preemptible_tcs(ocelot, port);
103 }
104
105 static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)
106 {
107         struct ocelot_port *ocelot_port = ocelot->ports[port];
108         struct ocelot_mm_state *mm = &ocelot->mm[port];
109         enum ethtool_mm_verify_status verify_status;
110         u32 val, ack = 0;
111
112         if (!mm->tx_enabled)
113                 return;
114
115         val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
116
117         verify_status = ocelot_mm_verify_status(val);
118         if (mm->verify_status != verify_status) {
119                 dev_dbg(ocelot->dev,
120                         "Port %d MAC Merge verification state %s\n",
121                         port, mm_verify_state_to_string(verify_status));
122                 mm->verify_status = verify_status;
123         }
124
125         if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
126                 mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
127
128                 dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
129                         port, mm->tx_active ? "active" : "inactive");
130                 ocelot_port_update_active_preemptible_tcs(ocelot, port);
131
132                 ack |= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY;
133         }
134
135         if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
136                 dev_err(ocelot->dev,
137                         "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
138                         port);
139
140                 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY;
141         }
142
143         if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
144                 dev_err(ocelot->dev,
145                         "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
146                         port);
147
148                 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY;
149         }
150
151         if (ack)
152                 ocelot_port_writel(ocelot_port, ack, DEV_MM_STATUS);
153 }
154
155 void ocelot_mm_irq(struct ocelot *ocelot)
156 {
157         int port;
158
159         mutex_lock(&ocelot->fwd_domain_lock);
160
161         for (port = 0; port < ocelot->num_phys_ports; port++)
162                 ocelot_mm_update_port_status(ocelot, port);
163
164         mutex_unlock(&ocelot->fwd_domain_lock);
165 }
166 EXPORT_SYMBOL_GPL(ocelot_mm_irq);
167
168 int ocelot_port_set_mm(struct ocelot *ocelot, int port,
169                        struct ethtool_mm_cfg *cfg,
170                        struct netlink_ext_ack *extack)
171 {
172         struct ocelot_port *ocelot_port = ocelot->ports[port];
173         u32 mm_enable = 0, verify_disable = 0, add_frag_size;
174         struct ocelot_mm_state *mm;
175         int err;
176
177         if (!ocelot->mm_supported)
178                 return -EOPNOTSUPP;
179
180         mm = &ocelot->mm[port];
181
182         err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
183                                               &add_frag_size, extack);
184         if (err)
185                 return err;
186
187         if (cfg->pmac_enabled)
188                 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
189
190         if (cfg->tx_enabled)
191                 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
192
193         if (!cfg->verify_enabled)
194                 verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
195
196         mutex_lock(&ocelot->fwd_domain_lock);
197
198         ocelot_port_rmwl(ocelot_port, mm_enable,
199                          DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
200                          DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
201                          DEV_MM_ENABLE_CONFIG);
202
203         ocelot_port_rmwl(ocelot_port, verify_disable |
204                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
205                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
206                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
207                          DEV_MM_VERIF_CONFIG);
208
209         ocelot_rmw_rix(ocelot,
210                        QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
211                        QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
212                        QSYS_PREEMPTION_CFG,
213                        port);
214
215         /* The switch will emit an IRQ when TX is disabled, to notify that it
216          * has become inactive. We optimize ocelot_mm_update_port_status() to
217          * not bother processing MM IRQs at all for ports with TX disabled,
218          * but we need to ACK this IRQ now, while mm->tx_enabled is still set,
219          * otherwise we get an IRQ storm.
220          */
221         if (mm->tx_enabled && !cfg->tx_enabled) {
222                 ocelot_mm_update_port_status(ocelot, port);
223                 WARN_ON(mm->tx_active);
224         }
225
226         mm->tx_enabled = cfg->tx_enabled;
227
228         mutex_unlock(&ocelot->fwd_domain_lock);
229
230         return 0;
231 }
232 EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
233
234 int ocelot_port_get_mm(struct ocelot *ocelot, int port,
235                        struct ethtool_mm_state *state)
236 {
237         struct ocelot_port *ocelot_port = ocelot->ports[port];
238         struct ocelot_mm_state *mm;
239         u32 val, add_frag_size;
240
241         if (!ocelot->mm_supported)
242                 return -EOPNOTSUPP;
243
244         mm = &ocelot->mm[port];
245
246         mutex_lock(&ocelot->fwd_domain_lock);
247
248         val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
249         state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
250         state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
251
252         val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
253         state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
254         state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
255         state->max_verify_time = 128;
256
257         val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
258         add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
259         state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
260         state->rx_min_frag_size = ETH_ZLEN;
261
262         ocelot_mm_update_port_status(ocelot, port);
263         state->verify_status = mm->verify_status;
264         state->tx_active = mm->tx_active;
265
266         mutex_unlock(&ocelot->fwd_domain_lock);
267
268         return 0;
269 }
270 EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
271
272 int ocelot_mm_init(struct ocelot *ocelot)
273 {
274         struct ocelot_port *ocelot_port;
275         struct ocelot_mm_state *mm;
276         int port;
277
278         if (!ocelot->mm_supported)
279                 return 0;
280
281         ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
282                                   sizeof(*ocelot->mm), GFP_KERNEL);
283         if (!ocelot->mm)
284                 return -ENOMEM;
285
286         for (port = 0; port < ocelot->num_phys_ports; port++) {
287                 u32 val;
288
289                 mm = &ocelot->mm[port];
290                 ocelot_port = ocelot->ports[port];
291
292                 /* Update initial status variable for the
293                  * verification state machine
294                  */
295                 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
296                 mm->verify_status = ocelot_mm_verify_status(val);
297         }
298
299         return 0;
300 }