3e458f72f645ed3d2c2ca3830daf0dbf7a191241
[platform/kernel/linux-starfive.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 static void ocelot_mm_update_port_status(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         enum ethtool_mm_verify_status verify_status;
57         u32 val, ack = 0;
58
59         if (!mm->tx_enabled)
60                 return;
61
62         val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
63
64         verify_status = ocelot_mm_verify_status(val);
65         if (mm->verify_status != verify_status) {
66                 dev_dbg(ocelot->dev,
67                         "Port %d MAC Merge verification state %s\n",
68                         port, mm_verify_state_to_string(verify_status));
69                 mm->verify_status = verify_status;
70         }
71
72         if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
73                 mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
74
75                 dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
76                         port, mm->tx_active ? "active" : "inactive");
77
78                 ack |= DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY;
79         }
80
81         if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
82                 dev_err(ocelot->dev,
83                         "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
84                         port);
85
86                 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY;
87         }
88
89         if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
90                 dev_err(ocelot->dev,
91                         "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
92                         port);
93
94                 ack |= DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY;
95         }
96
97         if (ack)
98                 ocelot_port_writel(ocelot_port, ack, DEV_MM_STATUS);
99 }
100
101 void ocelot_mm_irq(struct ocelot *ocelot)
102 {
103         int port;
104
105         mutex_lock(&ocelot->fwd_domain_lock);
106
107         for (port = 0; port < ocelot->num_phys_ports; port++)
108                 ocelot_mm_update_port_status(ocelot, port);
109
110         mutex_unlock(&ocelot->fwd_domain_lock);
111 }
112 EXPORT_SYMBOL_GPL(ocelot_mm_irq);
113
114 int ocelot_port_set_mm(struct ocelot *ocelot, int port,
115                        struct ethtool_mm_cfg *cfg,
116                        struct netlink_ext_ack *extack)
117 {
118         struct ocelot_port *ocelot_port = ocelot->ports[port];
119         u32 mm_enable = 0, verify_disable = 0, add_frag_size;
120         struct ocelot_mm_state *mm;
121         int err;
122
123         if (!ocelot->mm_supported)
124                 return -EOPNOTSUPP;
125
126         mm = &ocelot->mm[port];
127
128         err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
129                                               &add_frag_size, extack);
130         if (err)
131                 return err;
132
133         if (cfg->pmac_enabled)
134                 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
135
136         if (cfg->tx_enabled)
137                 mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
138
139         if (!cfg->verify_enabled)
140                 verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
141
142         mutex_lock(&ocelot->fwd_domain_lock);
143
144         ocelot_port_rmwl(ocelot_port, mm_enable,
145                          DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
146                          DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
147                          DEV_MM_ENABLE_CONFIG);
148
149         ocelot_port_rmwl(ocelot_port, verify_disable |
150                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
151                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
152                          DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
153                          DEV_MM_VERIF_CONFIG);
154
155         ocelot_rmw_rix(ocelot,
156                        QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
157                        QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
158                        QSYS_PREEMPTION_CFG,
159                        port);
160
161         /* The switch will emit an IRQ when TX is disabled, to notify that it
162          * has become inactive. We optimize ocelot_mm_update_port_status() to
163          * not bother processing MM IRQs at all for ports with TX disabled,
164          * but we need to ACK this IRQ now, while mm->tx_enabled is still set,
165          * otherwise we get an IRQ storm.
166          */
167         if (mm->tx_enabled && !cfg->tx_enabled) {
168                 ocelot_mm_update_port_status(ocelot, port);
169                 WARN_ON(mm->tx_active);
170         }
171
172         mm->tx_enabled = cfg->tx_enabled;
173
174         mutex_unlock(&ocelot->fwd_domain_lock);
175
176         return 0;
177 }
178 EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
179
180 int ocelot_port_get_mm(struct ocelot *ocelot, int port,
181                        struct ethtool_mm_state *state)
182 {
183         struct ocelot_port *ocelot_port = ocelot->ports[port];
184         struct ocelot_mm_state *mm;
185         u32 val, add_frag_size;
186
187         if (!ocelot->mm_supported)
188                 return -EOPNOTSUPP;
189
190         mm = &ocelot->mm[port];
191
192         mutex_lock(&ocelot->fwd_domain_lock);
193
194         val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
195         state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
196         state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
197
198         val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
199         state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
200         state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
201         state->max_verify_time = 128;
202
203         val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
204         add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
205         state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
206         state->rx_min_frag_size = ETH_ZLEN;
207
208         ocelot_mm_update_port_status(ocelot, port);
209         state->verify_status = mm->verify_status;
210         state->tx_active = mm->tx_active;
211
212         mutex_unlock(&ocelot->fwd_domain_lock);
213
214         return 0;
215 }
216 EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
217
218 int ocelot_mm_init(struct ocelot *ocelot)
219 {
220         struct ocelot_port *ocelot_port;
221         struct ocelot_mm_state *mm;
222         int port;
223
224         if (!ocelot->mm_supported)
225                 return 0;
226
227         ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
228                                   sizeof(*ocelot->mm), GFP_KERNEL);
229         if (!ocelot->mm)
230                 return -ENOMEM;
231
232         for (port = 0; port < ocelot->num_phys_ports; port++) {
233                 u32 val;
234
235                 mm = &ocelot->mm[port];
236                 ocelot_port = ocelot->ports[port];
237
238                 /* Update initial status variable for the
239                  * verification state machine
240                  */
241                 val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
242                 mm->verify_status = ocelot_mm_verify_status(val);
243         }
244
245         return 0;
246 }