1 /*
2 * Copyright (c) 2026 Justin Hibbits
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * FMan TX port driver.
7 */
8
9 #include <sys/param.h>
10 #include <sys/bus.h>
11 #include <sys/kernel.h>
12 #include <sys/module.h>
13
14 #include <dev/ofw/ofw_bus.h>
15 #include <dev/ofw/ofw_bus_subr.h>
16 #include <machine/bus.h>
17
18 #include "fman.h"
19 #include "fman_parser.h"
20 #include "fman_port.h"
21 #include "fman_port_var.h"
22 #include "fman_if.h"
23 #include "fman_port_if.h"
24
25 #define TX_10G_PORT_BASE 0x30
26
27 /* BMI Tx registers. */
28 #define FMBM_TCFG 0x000
29 #define FMBM_TST 0x004
30 #define FMBM_TDA 0x008
31 #define FMBM_TFP 0x00c
32 #define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12
33 #define FMBM_TFED 0x010
34 #define FMBM_TICP 0x014
35 #define TICP_ICEOF_M 0x001f0000
36 #define TICP_ICEOF_S 16
37 #define TICP_ICIOF_M 0x00000f00
38 #define TICP_ICIOF_S 8
39 #define TICP_ICSZ_S 0x0000001f
40 #define FMBM_TFDNE 0x018
41 #define FMBM_TFCA 0x01c
42 #define TFCA_MR_DEF 0
43 #define TFCA_ATTR_ORDER 0x80000000
44 #define FMBM_TCFQID 0x020
45 #define FMBM_TEFQID 0x024
46 #define FMBM_TFENE 0x028
47 #define FMBM_TFNE 0x070
48 #define TFNE_EBD 0x80000000
49
50 /* QMI dequeue-config (FMQM_PNDC) field layout: TX-only consumer. */
51 #define QMI_DEQ_CFG_PRI 0x80000000
52 #define QMI_DEQ_CFG_TYPE1 0x10000000
53 #define QMI_DEQ_CFG_TYPE2 0x20000000
54 #define QMI_DEQ_CFG_TYPE3 0x30000000
55 #define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000
56 #define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000
57 #define QMI_DEQ_CFG_SP_MASK 0xf
58 #define QMI_DEQ_CFG_SP_SHIFT 20
59
60 struct fman_port_tx_softc {
61 struct fman_port_softc sc_base;
62
63 int sc_deq_byte_count;
64 int sc_deq_high_priority;
65 int sc_tx_deq_pipeline_depth;
66 int sc_qman_channel_id;
67 };
68
69 static struct ofw_compat_data tx_compats[] = {
70 { "fsl,fman-v2-port-tx", 0 },
71 { "fsl,fman-v3-port-tx", PORT_V3 },
72 { NULL, 0 }
73 };
74
75 static int
fman_port_tx_probe(device_t dev)76 fman_port_tx_probe(device_t dev)
77 {
78
79 if (ofw_bus_search_compatible(dev, tx_compats)->ocd_str == NULL)
80 return (ENXIO);
81
82 device_set_desc(dev, "FMan TX port");
83 return (BUS_PROBE_DEFAULT);
84 }
85
86 static int
fman_port_tx_attach(device_t dev)87 fman_port_tx_attach(device_t dev)
88 {
89 struct fman_port_tx_softc *sc;
90 uintptr_t compat_data;
91 int port_speed;
92 int err;
93
94 sc = device_get_softc(dev);
95 compat_data = ofw_bus_search_compatible(dev, tx_compats)->ocd_data;
96
97 port_speed = 1000;
98 if ((compat_data & PORT_V3) == PORT_V3) {
99 if (OF_hasprop(ofw_bus_get_node(dev), "fsl,fman-10g-port"))
100 port_speed = 10000;
101 } else {
102 int cell = 0;
103 OF_getencprop(ofw_bus_get_node(dev), "cell-index", &cell,
104 sizeof(cell));
105 if (cell > TX_10G_PORT_BASE)
106 port_speed = 10000;
107 }
108
109 err = fman_port_attach_common(dev, &sc->sc_base, port_speed);
110 if (err != 0)
111 return (err);
112
113 if (port_speed == 10000) {
114 sc->sc_deq_high_priority = true;
115 sc->sc_deq_byte_count = 0x1400;
116 } else {
117 sc->sc_deq_high_priority = false;
118 sc->sc_deq_byte_count = 0x0400;
119 }
120
121 sc->sc_qman_channel_id = FMAN_GET_QMAN_CHANNEL_ID(
122 device_get_parent(dev), sc->sc_base.sc_port_id);
123
124 return (0);
125 }
126
127 static int
fman_port_tx_detach(device_t dev)128 fman_port_tx_detach(device_t dev)
129 {
130 struct fman_port_tx_softc *sc = device_get_softc(dev);
131
132 return (fman_port_detach_common(dev, &sc->sc_base));
133 }
134
135 static int
fman_port_tx_config(device_t dev,struct fman_port_params * params)136 fman_port_tx_config(device_t dev, struct fman_port_params *params)
137 {
138 struct fman_port_tx_softc *sc = device_get_softc(dev);
139 struct fman_port_softc *base = &sc->sc_base;
140
141 fman_port_config_common(base, params);
142
143 if (base->sc_revision_major >= 6 && base->sc_port_speed == 1000)
144 /* Errata A005127 workaround */
145 bus_write_4(base->sc_mem, FMBM_TFP, 0x00001013);
146
147 base->sc_tasks.extra = 0;
148 switch (base->sc_port_speed) {
149 case 10000:
150 base->sc_tasks.num = (base->sc_revision_major < 6) ? 16 : 14;
151 break;
152 case 1000:
153 base->sc_tasks.num = (base->sc_revision_major >= 6) ? 4 : 3;
154 break;
155 default:
156 base->sc_tasks.num = 0;
157 break;
158 }
159
160 if (base->sc_revision_major >= 6) {
161 base->sc_open_dmas.extra = 0;
162 base->sc_open_dmas.num =
163 (base->sc_port_speed == 10000) ? 12 : 3;
164 } else if (base->sc_port_speed == 10000) {
165 base->sc_open_dmas.num = 8;
166 base->sc_open_dmas.extra = 8;
167 } else {
168 base->sc_open_dmas.num = 1;
169 base->sc_open_dmas.extra = 1;
170 }
171
172 if (base->sc_revision_major >= 6)
173 base->sc_fifo_bufs.num =
174 (base->sc_port_speed == 10000) ? 64 : 50;
175 else
176 base->sc_fifo_bufs.num =
177 (base->sc_port_speed == 10000) ? 48 : 44;
178 base->sc_fifo_bufs.extra = 0;
179 base->sc_fifo_bufs.num *= FMAN_BMI_FIFO_UNITS;
180
181 /* TODO: buf_margins? See fman_sp_build_buffer_struct */
182 return (0);
183 }
184
185 static int
fman_port_tx_init_bmi(struct fman_port_tx_softc * sc)186 fman_port_tx_init_bmi(struct fman_port_tx_softc *sc)
187 {
188 struct fman_port_softc *base = &sc->sc_base;
189 uint32_t reg;
190 int depth;
191
192 bus_write_4(base->sc_mem, FMBM_TCFG, 0);
193 bus_write_4(base->sc_mem, FMBM_TDA, 0);
194 bus_write_4(base->sc_mem, FMBM_TFED, 0);
195
196 if (base->sc_port_speed == 10000)
197 depth = 4;
198 else if (base->sc_revision_major >= 6)
199 depth = 2;
200 else
201 depth = 1;
202 sc->sc_tx_deq_pipeline_depth = depth;
203 reg = ((depth - 1) << BMI_FIFO_PIPELINE_DEPTH_SHIFT) | 0x13;
204 bus_write_4(base->sc_mem, FMBM_TFP, reg);
205
206 /* Default color: green */
207 bus_write_4(base->sc_mem, FMBM_TFCA, TFCA_MR_DEF | TFCA_ATTR_ORDER);
208
209 bus_write_4(base->sc_mem, FMBM_TFDNE, NIA_ENG_QMI_DEQ);
210 bus_write_4(base->sc_mem, FMBM_TFENE,
211 NIA_ENG_QMI_ENQ | NIA_ORDER_RESTORE);
212
213 /* Insert internal context ahead of the frame */
214 reg = howmany(FMAN_PARSE_RESULT_OFF, 0x10) << TICP_ICIOF_S;
215 reg |= howmany(sizeof(struct fman_internal_context), 0x10);
216 bus_write_4(base->sc_mem, FMBM_TICP, reg);
217
218 if (base->sc_revision_major >= 6)
219 bus_write_4(base->sc_mem, FMBM_TFNE,
220 (base->sc_default_fqid == 0 ? TFNE_EBD : 0) |
221 NIA_BMI_AC_FETCH_ALLFRAME);
222 bus_write_4(base->sc_mem, FMBM_TCFQID, base->sc_default_fqid);
223 bus_write_4(base->sc_mem, FMBM_TEFQID, base->sc_err_fqid);
224
225 return (0);
226 }
227
228 static int
fman_port_tx_init_qmi(struct fman_port_tx_softc * sc)229 fman_port_tx_init_qmi(struct fman_port_tx_softc *sc)
230 {
231 struct fman_port_softc *base = &sc->sc_base;
232 uint32_t reg;
233
234 bus_write_4(base->sc_mem, FMQM_PNDN, NIA_ENG_BMI | NIA_BMI_AC_TX);
235 bus_write_4(base->sc_mem, FMQM_PNEN,
236 NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE);
237
238 reg = 0;
239 if (sc->sc_deq_high_priority)
240 reg |= QMI_DEQ_CFG_PRI;
241 reg |= QMI_DEQ_CFG_TYPE1;
242 reg |= QMI_DEQ_CFG_PREFETCH_FULL;
243 reg |= (sc->sc_qman_channel_id & QMI_DEQ_CFG_SP_MASK) <<
244 QMI_DEQ_CFG_SP_SHIFT;
245 reg |= sc->sc_deq_byte_count;
246 bus_write_4(base->sc_mem, FMQM_PNDC, reg);
247
248 return (0);
249 }
250
251 static int
fman_port_tx_init(device_t dev)252 fman_port_tx_init(device_t dev)
253 {
254 struct fman_port_tx_softc *sc = device_get_softc(dev);
255 struct fman_port_softc *base = &sc->sc_base;
256 struct fman_port_init_params params;
257 int err;
258
259 params.port_id = base->sc_port_id;
260 params.is_rx_port = false;
261 params.num_tasks = base->sc_tasks.num;
262 params.extra_tasks = base->sc_tasks.extra;
263 params.open_dmas = base->sc_open_dmas.num;
264 params.extra_dmas = base->sc_open_dmas.extra;
265 params.fifo_size = base->sc_fifo_bufs.num;
266 params.extra_fifo_size = base->sc_fifo_bufs.extra;
267 params.max_frame_length = base->sc_max_frame_length;
268 params.deq_pipeline_size = sc->sc_tx_deq_pipeline_depth;
269
270 /* TODO: verify_size_of_fifo() from Linux driver */
271 err = FMAN_SET_PORT_PARAMS(device_get_parent(dev), ¶ms);
272 if (err != 0)
273 return (err);
274
275 err = fman_port_tx_init_bmi(sc);
276 if (err != 0)
277 return (err);
278
279 return (fman_port_tx_init_qmi(sc));
280 }
281
282 static int
fman_port_tx_disable(device_t dev)283 fman_port_tx_disable(device_t dev)
284 {
285 struct fman_port_tx_softc *sc = device_get_softc(dev);
286 struct fman_port_softc *base = &sc->sc_base;
287 uint32_t reg;
288 int count;
289
290 reg = bus_read_4(base->sc_mem, FMQM_PNC);
291 bus_write_4(base->sc_mem, FMQM_PNC, reg & ~PNC_EN);
292 for (count = 0; count < 100; count++) {
293 DELAY(10);
294 reg = bus_read_4(base->sc_mem, FMQM_PNS);
295 if (!(reg & PNS_DEQ_FD_BSY))
296 break;
297 }
298 if (count == 100)
299 device_printf(base->sc_dev, "Timeout stopping QMI\n");
300
301 reg = bus_read_4(base->sc_mem, FMBM_TCFG);
302 bus_write_4(base->sc_mem, FMBM_TCFG, reg & ~BMI_PORT_CFG_EN);
303 for (count = 0; count < 100; count++) {
304 DELAY(10);
305 reg = bus_read_4(base->sc_mem, FMBM_TST);
306 if (!(reg & PNS_DEQ_FD_BSY))
307 break;
308 }
309 if (count == 100)
310 device_printf(base->sc_dev, "Timeout stopping BMI\n");
311
312 return (0);
313 }
314
315 static int
fman_port_tx_enable(device_t dev)316 fman_port_tx_enable(device_t dev)
317 {
318 struct fman_port_tx_softc *sc = device_get_softc(dev);
319 struct fman_port_softc *base = &sc->sc_base;
320 uint32_t reg;
321
322 reg = bus_read_4(base->sc_mem, FMQM_PNC);
323 bus_write_4(base->sc_mem, FMQM_PNC, reg | PNC_EN | PNC_STEN);
324
325 reg = bus_read_4(base->sc_mem, FMBM_TCFG);
326 bus_write_4(base->sc_mem, FMBM_TCFG, reg | BMI_PORT_CFG_EN);
327
328 return (0);
329 }
330
331 static device_method_t fman_port_tx_methods[] = {
332 DEVMETHOD(device_probe, fman_port_tx_probe),
333 DEVMETHOD(device_attach, fman_port_tx_attach),
334 DEVMETHOD(device_detach, fman_port_tx_detach),
335
336 DEVMETHOD(fman_port_config, fman_port_tx_config),
337 DEVMETHOD(fman_port_init, fman_port_tx_init),
338 DEVMETHOD(fman_port_enable, fman_port_tx_enable),
339 DEVMETHOD(fman_port_disable, fman_port_tx_disable),
340
341 DEVMETHOD_END
342 };
343
344 DEFINE_CLASS_0(fman_port_tx, fman_port_tx_driver, fman_port_tx_methods,
345 sizeof(struct fman_port_tx_softc));
346 EARLY_DRIVER_MODULE(fman_port_tx, fman, fman_port_tx_driver, 0, 0,
347 BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
348