blob: 59be063657a91d9f14a0811f454f111f20198914 [file] [log] [blame]
Sylvain Munautbc9f5c42020-09-14 10:22:29 +02001/*
2 * e1.c
3 *
4 * Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
5 * SPDX-License-Identifier: GPL-3.0-or-later
6 */
7
8#include <stdint.h>
9#include <stdbool.h>
10#include <string.h>
11
12#include "config.h"
13#include "console.h"
14#include "e1.h"
Harald Weltef74dad72020-12-15 23:32:53 +010015#include "e1_hw.h"
Sylvain Munautbc9f5c42020-09-14 10:22:29 +020016
17#include "dma.h"
18#include "led.h" // FIXME
19
20
Sylvain Munautbc9f5c42020-09-14 10:22:29 +020021static volatile struct e1_core * const e1_regs = (void *)(E1_CORE_BASE);
22static volatile uint8_t * const e1_data = (void *)(E1_DATA_BASE);
23
Sylvain Munaut35856a12022-01-03 18:45:53 +010024static unsigned int
Sylvain Munautbc9f5c42020-09-14 10:22:29 +020025e1_data_ofs(int mf, int frame, int ts)
26{
27 return (mf << 9) | (frame << 5) | ts;
28}
29
Sylvain Munaut35856a12022-01-03 18:45:53 +010030static volatile uint8_t *
Harald Weltea59ef2b2020-12-14 17:02:13 +010031e1_data_ptr(int mf, int frame, int ts)
32{
33 return &e1_data[e1_data_ofs(mf, frame, ts)];
34}
Sylvain Munautbc9f5c42020-09-14 10:22:29 +020035
36// FIFOs
37// -----
38/* Note: FIFO works at 'frame' level (i.e. 32 bytes) */
39
40struct e1_fifo {
41 /* Buffer zone associated with the FIFO */
42 unsigned int base;
43 unsigned int mask;
44
45 /* Pointers / Levels */
46 unsigned int wptr[2]; /* 0=committed 1=allocated */
47 unsigned int rptr[2]; /* 0=discared 1=peeked */
48};
49
50 /* Utils */
51static void
52e1f_reset(struct e1_fifo *fifo, unsigned int base, unsigned int len)
53{
54 memset(fifo, 0x00, sizeof(struct e1_fifo));
55 fifo->base = base;
56 fifo->mask = len - 1;
57}
58
59static unsigned int
60e1f_allocd_frames(struct e1_fifo *fifo)
61{
62 /* Number of frames that are allocated (i.e. where we can't write to) */
63 return (fifo->wptr[1] - fifo->rptr[0]) & fifo->mask;
64}
65
66static unsigned int
67e1f_valid_frames(struct e1_fifo *fifo)
68{
69 /* Number of valid frames */
70 return (fifo->wptr[0] - fifo->rptr[0]) & fifo->mask;
71}
72
73static unsigned int
74e1f_unseen_frames(struct e1_fifo *fifo)
75{
76 /* Number of valid frames that haven't been peeked yet */
77 return (fifo->wptr[0] - fifo->rptr[1]) & fifo->mask;
78}
79
80static unsigned int
81e1f_free_frames(struct e1_fifo *fifo)
82{
83 /* Number of frames that aren't allocated */
84 return (fifo->rptr[0] - fifo->wptr[1] - 1) & fifo->mask;
85}
86
87static unsigned int
88e1f_ofs_to_dma(unsigned int ofs)
89{
90 /* DMA address are 32-bits word address. Offsets are 32 byte address */
91 return (ofs << 3);
92}
93
94static unsigned int
95e1f_ofs_to_mf(unsigned int ofs)
96{
97 /* E1 Buffer Descriptors are always multiframe aligned */
98 return (ofs >> 4);
99}
100
101
102 /* Debug */
103static void
104e1f_debug(struct e1_fifo *fifo, const char *name)
105{
106 unsigned int la, lv, lu, lf;
107
108 la = e1f_allocd_frames(fifo);
109 lv = e1f_valid_frames(fifo);
110 lu = e1f_unseen_frames(fifo);
111 lf = e1f_free_frames(fifo);
112
113 printf("%s: R: %u / %u | W: %u / %u | A:%u V:%u U:%u F:%u\n",
114 name,
115 fifo->rptr[0], fifo->rptr[1], fifo->wptr[0], fifo->wptr[1],
116 la, lv, lu, lf
117 );
118}
119
120 /* Frame level read/write */
121static unsigned int
122e1f_frame_write(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames)
123{
124 unsigned int lf, le;
125
126 lf = e1f_free_frames(fifo);
127 le = fifo->mask - fifo->wptr[0] + 1;
128
129 if (max_frames > le)
130 max_frames = le;
131 if (max_frames > lf)
132 max_frames = lf;
133
134 *ofs = fifo->base + fifo->wptr[0];
135 fifo->wptr[1] = fifo->wptr[0] = (fifo->wptr[0] + max_frames) & fifo->mask;
136
137 return max_frames;
138}
139
140static unsigned int
Sylvain Munautde20fb72020-10-29 13:24:50 +0100141e1f_frame_read(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames)
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200142{
143 unsigned int lu, le;
144
145 lu = e1f_unseen_frames(fifo);
146 le = fifo->mask - fifo->rptr[1] + 1;
147
148 if (max_frames > le)
149 max_frames = le;
150 if (max_frames > lu)
151 max_frames = lu;
152
153 *ofs = fifo->base + fifo->rptr[1];
154 fifo->rptr[0] = fifo->rptr[1] = (fifo->rptr[1] + max_frames) & fifo->mask;
155
156 return max_frames;
157}
158
159
160 /* MultiFrame level split read/write */
161static bool
162e1f_multiframe_write_prepare(struct e1_fifo *fifo, unsigned int *ofs)
163{
164 unsigned int lf;
165
166 lf = e1f_free_frames(fifo);
167 if (lf < 16)
168 return false;
169
170 *ofs = fifo->base + fifo->wptr[1];
171 fifo->wptr[1] = (fifo->wptr[1] + 16) & fifo->mask;
172
173 return true;
174}
175
176static void
177e1f_multiframe_write_commit(struct e1_fifo *fifo)
178{
179 fifo->wptr[0] = (fifo->wptr[0] + 16) & fifo->mask;
180}
181
182static bool
183e1f_multiframe_read_peek(struct e1_fifo *fifo, unsigned int *ofs)
184{
185 unsigned int lu;
186
187 lu = e1f_unseen_frames(fifo);
188 if (lu < 16)
189 return false;
190
191 *ofs = fifo->base + fifo->rptr[1];
192 fifo->rptr[1] = (fifo->rptr[1] + 16) & fifo->mask;
193
194 return true;
195}
196
197static void
198e1f_multiframe_read_discard(struct e1_fifo *fifo)
199{
200 fifo->rptr[0] = (fifo->rptr[0] + 16) & fifo->mask;
201}
202
203static void
204e1f_multiframe_empty(struct e1_fifo *fifo)
205{
206 fifo->rptr[0] = fifo->rptr[1] = (fifo->wptr[0] & ~15);
207}
208
209
210
211// Main logic
212// ----------
213
214enum e1_pipe_state {
Harald Welte30fc5602020-12-14 15:56:28 +0100215 IDLE = 0, /* not yet initialized */
216 BOOT = 1, /* after e1_init(), regiters are programmed */
217 RUN = 2, /* normal operation */
218 RECOVER = 3, /* after underflow, overflow or alignment error */
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200219};
220
221static struct {
222 struct {
223 uint32_t cr;
224 struct e1_fifo fifo;
225 int in_flight;
226 enum e1_pipe_state state;
227 } rx;
228
229 struct {
230 uint32_t cr;
231 struct e1_fifo fifo;
232 int in_flight;
233 enum e1_pipe_state state;
234 } tx;
Harald Welte805f2cf2020-12-14 17:31:03 +0100235 struct e1_error_count errors;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200236} g_e1;
237
238
239
240
241void
Harald Welte9469e042020-12-15 23:09:40 +0100242e1_init(uint16_t rx_cr, uint16_t tx_cr)
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200243{
244 /* Global state init */
245 memset(&g_e1, 0x00, sizeof(g_e1));
246
247 /* Reset FIFOs */
248 e1f_reset(&g_e1.rx.fifo, 0, 128);
249 e1f_reset(&g_e1.tx.fifo, 128, 128);
250
251 /* Enable Rx */
Harald Welte9469e042020-12-15 23:09:40 +0100252 g_e1.rx.cr = E1_RX_CR_ENABLE | rx_cr;
Harald Welte7f74fe62020-12-27 14:33:37 +0100253 e1_regs->rx.csr = E1_RX_CR_OVFL_CLR | g_e1.rx.cr;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200254
255 /* Enable Tx */
Harald Welte9469e042020-12-15 23:09:40 +0100256 g_e1.tx.cr = E1_TX_CR_ENABLE | tx_cr;
Harald Welte7f74fe62020-12-27 14:33:37 +0100257 e1_regs->tx.csr = E1_TX_CR_UNFL_CLR | g_e1.tx.cr;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200258
259 /* State */
260 g_e1.rx.state = BOOT;
261 g_e1.tx.state = BOOT;
262}
263
Harald Welte6add0aa2020-12-16 00:02:11 +0100264#define TXCR_PERMITTED ( \
265 E1_TX_CR_MODE_TS0_CRC_E | \
266 E1_TX_CR_TICK_REMOTE | \
267 E1_TX_CR_ALARM | \
268 E1_TX_CR_LOOPBACK | \
269 E1_TX_CR_LOOPBACK_CROSS )
270
271void
272e1_tx_config(uint16_t cr)
273{
274 g_e1.tx.cr = (g_e1.tx.cr & ~TXCR_PERMITTED) | (cr & TXCR_PERMITTED);
275 e1_regs->tx.csr = g_e1.tx.cr;
276}
277
278#define RXCR_PERMITTED ( \
279 E1_RX_CR_MODE_MFA )
280
281void
282e1_rx_config(uint16_t cr)
283{
284 g_e1.rx.cr = (g_e1.rx.cr & ~RXCR_PERMITTED) | (cr & RXCR_PERMITTED);
285 e1_regs->rx.csr = g_e1.rx.cr;
286}
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200287
288#include "dma.h"
289
290unsigned int
Harald Weltedaff4f62020-12-14 17:39:23 +0100291e1_rx_need_data(unsigned int usb_addr, unsigned int max_frames, unsigned int *pos)
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200292{
Harald Welte51baa362022-01-01 15:22:25 +0100293 bool rai_received = false;
294 bool rai_possible = false;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200295 unsigned int ofs;
296 int tot_frames = 0;
Harald Welte51baa362022-01-01 15:22:25 +0100297 int n_frames, i;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200298
299 while (max_frames) {
300 /* Get some data from the FIFO */
301 n_frames = e1f_frame_read(&g_e1.rx.fifo, &ofs, max_frames);
302 if (!n_frames)
303 break;
304
Harald Weltedaff4f62020-12-14 17:39:23 +0100305 /* Give pos */
306 if (pos) {
307 *pos = ofs & g_e1.rx.fifo.mask;
308 pos = NULL;
309 }
310
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200311 /* Copy from FIFO to USB */
312 dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), false, NULL, NULL);
313
314 /* Prepare Next */
315 usb_addr += n_frames * (32 / 4);
316 max_frames -= n_frames;
317 tot_frames += n_frames;
318
Harald Welte51baa362022-01-01 15:22:25 +0100319 /* While DMA is running: Determine if remote end indicates any alarms */
320 for (i = 0; i < n_frames; i++) {
321 unsigned int frame_nr = ofs + i;
322 /* A bit is present in every odd frame TS0 */
323 if (frame_nr & 1) {
324 uint8_t ts0 = *e1_data_ptr(0, ofs + i, 0);
325 rai_possible = true;
326 if (ts0 & 0x20) {
327 rai_received = true;
328 break;
329 }
330 }
331 }
332
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200333 /* Wait for DMA completion */
334 while (dma_poll());
335 }
336
Harald Welte51baa362022-01-01 15:22:25 +0100337 if (rai_possible) {
338 if (rai_received) {
339 g_e1.errors.flags |= E1_ERR_F_RAI;
340 e1_platform_led_set(0, E1P_LED_YELLOW, E1P_LED_ST_ON);
341 } else {
342 g_e1.errors.flags &= ~E1_ERR_F_RAI;
343 e1_platform_led_set(0, E1P_LED_YELLOW, E1P_LED_ST_OFF);
344 }
345 }
346
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200347 return tot_frames;
348}
349
350unsigned int
351e1_tx_feed_data(unsigned int usb_addr, unsigned int frames)
352{
353 unsigned int ofs;
354 int n_frames;
355
356 while (frames) {
357 /* Get some space in FIFO */
358 n_frames = e1f_frame_write(&g_e1.tx.fifo, &ofs, frames);
359 if (!n_frames) {
360 printf("[!] TX FIFO Overflow %d %d\n", frames, n_frames);
361 break;
362 }
363
364 /* Copy from USB to FIFO */
365 dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), true, NULL, NULL);
366
367 /* Prepare next */
368 usb_addr += n_frames * (32 / 4);
369 frames -= n_frames;
370
371 /* Wait for DMA completion */
372 while (dma_poll());
373 }
374
375 return frames;
376}
377
378unsigned int
379e1_tx_level(void)
380{
381 return e1f_valid_frames(&g_e1.tx.fifo);
382}
383
384unsigned int
385e1_rx_level(void)
386{
387 return e1f_valid_frames(&g_e1.rx.fifo);
388}
389
Harald Welte805f2cf2020-12-14 17:31:03 +0100390const struct e1_error_count *
391e1_get_error_count(void)
392{
393 return &g_e1.errors;
394}
395
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200396void
397e1_poll(void)
398{
399 uint32_t bd;
400 unsigned int ofs;
401
402 /* Active ? */
403 if ((g_e1.rx.state == IDLE) && (g_e1.tx.state == IDLE))
404 return;
405
406 /* HACK: LED link status */
Harald Welte52765672020-12-15 18:35:42 +0100407 if (e1_regs->rx.csr & E1_RX_SR_ALIGNED) {
408 e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_ON);
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200409 led_color(0, 48, 0);
Harald Welte805f2cf2020-12-14 17:31:03 +0100410 g_e1.errors.flags &= ~(E1_ERR_F_LOS|E1_ERR_F_ALIGN_ERR);
Harald Welte52765672020-12-15 18:35:42 +0100411 } else {
412 e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_BLINK);
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200413 led_color(48, 0, 0);
Harald Welte805f2cf2020-12-14 17:31:03 +0100414 g_e1.errors.flags |= E1_ERR_F_ALIGN_ERR;
415 /* TODO: completely off if rx tick counter not incrementing */
Harald Welte52765672020-12-15 18:35:42 +0100416 }
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200417
418 /* Recover any done TX BD */
419 while ( (bd = e1_regs->tx.bd) & E1_BD_VALID ) {
420 e1f_multiframe_read_discard(&g_e1.tx.fifo);
421 g_e1.tx.in_flight--;
422 }
423
424 /* Recover any done RX BD */
425 while ( (bd = e1_regs->rx.bd) & E1_BD_VALID ) {
426 /* FIXME: CRC status ? */
427 e1f_multiframe_write_commit(&g_e1.rx.fifo);
Harald Welte805f2cf2020-12-14 17:31:03 +0100428 if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) {
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200429 printf("b: %03x\n", bd);
Harald Welte805f2cf2020-12-14 17:31:03 +0100430 g_e1.errors.crc++;
431 }
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200432 g_e1.rx.in_flight--;
433 }
434
435 /* Boot procedure */
436 if (g_e1.tx.state == BOOT) {
437 if (e1f_unseen_frames(&g_e1.tx.fifo) < (16 * 5))
438 return;
439 /* HACK: LED flow status */
440 led_blink(true, 200, 1000);
441 led_breathe(true, 100, 200);
442 }
443
444 /* Handle RX */
445 /* Misalign ? */
446 if (g_e1.rx.state == RUN) {
447 if (!(e1_regs->rx.csr & E1_RX_SR_ALIGNED)) {
448 printf("[!] E1 rx misalign\n");
449 g_e1.rx.state = RECOVER;
Harald Welte805f2cf2020-12-14 17:31:03 +0100450 g_e1.errors.align++;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200451 }
452 }
453
454 /* Overflow ? */
455 if (g_e1.rx.state == RUN) {
456 if (e1_regs->rx.csr & E1_RX_SR_OVFL) {
457 printf("[!] E1 overflow %d\n", g_e1.rx.in_flight);
458 g_e1.rx.state = RECOVER;
Harald Welte805f2cf2020-12-14 17:31:03 +0100459 g_e1.errors.ovfl++;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200460 }
461 }
462
463 /* Recover ready ? */
464 if (g_e1.rx.state == RECOVER) {
465 if (g_e1.rx.in_flight != 0)
466 goto done_rx;
467 e1f_multiframe_empty(&g_e1.rx.fifo);
468 }
469
470 /* Fill new RX BD */
471 while (g_e1.rx.in_flight < 4) {
472 if (!e1f_multiframe_write_prepare(&g_e1.rx.fifo, &ofs))
473 break;
474 e1_regs->rx.bd = e1f_ofs_to_mf(ofs);
475 g_e1.rx.in_flight++;
476 }
477
478 /* Clear overflow if needed */
479 if (g_e1.rx.state != RUN) {
480 e1_regs->rx.csr = g_e1.rx.cr | E1_RX_CR_OVFL_CLR;
481 g_e1.rx.state = RUN;
482 }
483done_rx:
484
485 /* Handle TX */
486 /* Underflow ? */
487 if (g_e1.tx.state == RUN) {
488 if (e1_regs->tx.csr & E1_TX_SR_UNFL) {
489 printf("[!] E1 underflow %d\n", g_e1.tx.in_flight);
490 g_e1.tx.state = RECOVER;
Harald Welte805f2cf2020-12-14 17:31:03 +0100491 g_e1.errors.unfl++;
Sylvain Munautbc9f5c42020-09-14 10:22:29 +0200492 }
493 }
494
495 /* Recover ready ? */
496 if (g_e1.tx.state == RECOVER) {
497 if (e1f_unseen_frames(&g_e1.tx.fifo) < (16 * 5))
498 return;
499 }
500
501 /* Fill new TX BD */
502 while (g_e1.tx.in_flight < 4) {
503 if (!e1f_multiframe_read_peek(&g_e1.tx.fifo, &ofs))
504 break;
505 e1_regs->tx.bd = e1f_ofs_to_mf(ofs);
506 g_e1.tx.in_flight++;
507 }
508
509 /* Clear underflow if needed */
510 if (g_e1.tx.state != RUN) {
511 e1_regs->tx.csr = g_e1.tx.cr | E1_TX_CR_UNFL_CLR;
512 g_e1.tx.state = RUN;
513 }
514}
515
516void
517e1_debug_print(bool data)
518{
519 volatile uint8_t *p;
520
521 puts("E1\n");
522 printf("CSR: Rx %04x / Tx %04x\n", e1_regs->rx.csr, e1_regs->tx.csr);
523 printf("InF: Rx %d / Tx %d\n", g_e1.rx.in_flight, g_e1.tx.in_flight);
524 printf("Sta: Rx %d / Tx %d\n", g_e1.rx.state, g_e1.tx.state);
525
526 e1f_debug(&g_e1.rx.fifo, "Rx FIFO");
527 e1f_debug(&g_e1.tx.fifo, "Tx FIFO");
528
529 if (data) {
530 puts("\nE1 Data\n");
531 for (int f=0; f<16; f++) {
532 p = e1_data_ptr(0, f, 0);
533 for (int ts=0; ts<32; ts++)
534 printf(" %02x", p[ts]);
535 printf("\n");
536 }
537 }
538}