From 805f2cf16e0e7cdf78edcbbf001fe75889a5a912 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 14 Dec 2020 17:31:03 +0100 Subject: [PATCH] icE1usb fw: Expose error conditions from E1 driver This will allow the USB interface code to report the errors to the host PC. Related: OS#4674 Change-Id: Iba3e00a2b28a2fef6dbd986bfc706c1619c3a3ed --- firmware/ice40-riscv/icE1usb/e1.c | 18 ++++++++++-- firmware/ice40-riscv/icE1usb/e1.h | 13 +++++++++ firmware/ice40-riscv/icE1usb/ice1usb_proto.h | 29 +++++++++++++++++++ firmware/ice40-riscv/icE1usb/usb_desc_app.c | 22 ++++++++++++-- firmware/ice40-riscv/icE1usb/usb_e1.c | 30 ++++++++++++++++++++ 5 files changed, 108 insertions(+), 4 deletions(-) diff --git a/firmware/ice40-riscv/icE1usb/e1.c b/firmware/ice40-riscv/icE1usb/e1.c index 16c8c80..5e57480 100644 --- a/firmware/ice40-riscv/icE1usb/e1.c +++ b/firmware/ice40-riscv/icE1usb/e1.c @@ -232,6 +232,7 @@ static struct { int in_flight; enum e1_pipe_state state; } tx; + struct e1_error_count errors; } g_e1; @@ -360,6 +361,12 @@ e1_rx_level(void) return e1f_valid_frames(&g_e1.rx.fifo); } +const struct e1_error_count * +e1_get_error_count(void) +{ + return &g_e1.errors; +} + void e1_poll(void) { @@ -374,10 +381,12 @@ e1_poll(void) if (e1_regs->rx.csr & E1_RX_SR_ALIGNED) { e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_ON); led_color(0, 48, 0); + g_e1.errors.flags &= ~(E1_ERR_F_LOS|E1_ERR_F_ALIGN_ERR); } else { e1_platform_led_set(0, E1P_LED_GREEN, E1P_LED_ST_BLINK); - /* TODO: completely off if rx tick counter not incrementing */ led_color(48, 0, 0); + g_e1.errors.flags |= E1_ERR_F_ALIGN_ERR; + /* TODO: completely off if rx tick counter not incrementing */ } /* Recover any done TX BD */ @@ -390,8 +399,10 @@ e1_poll(void) while ( (bd = e1_regs->rx.bd) & E1_BD_VALID ) { /* FIXME: CRC status ? */ e1f_multiframe_write_commit(&g_e1.rx.fifo); - if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) + if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) { printf("b: %03x\n", bd); + g_e1.errors.crc++; + } g_e1.rx.in_flight--; } @@ -410,6 +421,7 @@ e1_poll(void) if (!(e1_regs->rx.csr & E1_RX_SR_ALIGNED)) { printf("[!] E1 rx misalign\n"); g_e1.rx.state = RECOVER; + g_e1.errors.align++; } } @@ -418,6 +430,7 @@ e1_poll(void) if (e1_regs->rx.csr & E1_RX_SR_OVFL) { printf("[!] E1 overflow %d\n", g_e1.rx.in_flight); g_e1.rx.state = RECOVER; + g_e1.errors.ovfl++; } } @@ -449,6 +462,7 @@ done_rx: if (e1_regs->tx.csr & E1_TX_SR_UNFL) { printf("[!] E1 underflow %d\n", g_e1.tx.in_flight); g_e1.tx.state = RECOVER; + g_e1.errors.unfl++; } } diff --git a/firmware/ice40-riscv/icE1usb/e1.h b/firmware/ice40-riscv/icE1usb/e1.h index fcd4284..8ba9838 100644 --- a/firmware/ice40-riscv/icE1usb/e1.h +++ b/firmware/ice40-riscv/icE1usb/e1.h @@ -14,6 +14,19 @@ void e1_debug_print(bool data); void e1_tx_config(uint16_t cr); void e1_rx_config(uint16_t cr); +#define E1_ERR_F_ALIGN_ERR 0x01 +#define E1_ERR_F_LOS 0x02 + +struct e1_error_count { + uint16_t crc; + uint16_t align; + uint16_t ovfl; + uint16_t unfl; + uint8_t flags; +}; + +const struct e1_error_count *e1_get_error_count(void); + volatile uint8_t *e1_data_ptr(int mf, int frame, int ts); unsigned int e1_data_ofs(int mf, int frame, int ts); diff --git a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h index 61e12ad..71f7236 100644 --- a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h +++ b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h @@ -75,3 +75,32 @@ enum ice1usb_rx_mode { struct ice1usb_rx_config { uint8_t mode; /*!< enum ice1usb_rx_mode */ } __attribute__((packed)); + + +/*********************************************************************** + * Interrupt Endpoint + ***********************************************************************/ + +enum ice1usb_irq_type { + ICE1USB_IRQ_T_ERRCNT = 1, +}; + +/* Ensue ro keep those in sync with e1.h */ +#define ICE1USB_ERR_F_ALIGN_ERR 0x01 +#define ICE1USB_ERR_F_LOS 0x02 + +struct ice1usb_irq_err { + /* 16-bit little-endian counters */ + uint16_t crc; + uint16_t align; + uint16_t ovfl; + uint16_t unfl; + uint8_t flags; +} __attribute__((packed)); + +struct ice1usb_irq { + uint8_t type; /*!< enum ice1usb_irq_type */ + union { + struct ice1usb_irq_err errors; + } u; +} __attribute__((packed)); diff --git a/firmware/ice40-riscv/icE1usb/usb_desc_app.c b/firmware/ice40-riscv/icE1usb/usb_desc_app.c index 9fe3968..153d70e 100644 --- a/firmware/ice40-riscv/icE1usb/usb_desc_app.c +++ b/firmware/ice40-riscv/icE1usb/usb_desc_app.c @@ -28,12 +28,14 @@ static const struct { struct usb_ep_desc ep_data_in; struct usb_ep_desc ep_data_out; struct usb_ep_desc ep_fb; + struct usb_ep_desc ep_interrupt; } __attribute__ ((packed)) off; struct { struct usb_intf_desc intf; struct usb_ep_desc ep_data_in; struct usb_ep_desc ep_data_out; struct usb_ep_desc ep_fb; + struct usb_ep_desc ep_interrupt; } __attribute__ ((packed)) on; } __attribute__ ((packed)) e1; @@ -79,7 +81,7 @@ static const struct { .bDescriptorType = USB_DT_INTF, .bInterfaceNumber = 0, .bAlternateSetting = 0, - .bNumEndpoints = 3, + .bNumEndpoints = 4, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0xe1, .bInterfaceProtocol = 0x00, @@ -109,6 +111,14 @@ static const struct { .wMaxPacketSize = 0, .bInterval = 3, }, + .ep_interrupt = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x83, + .bmAttributes = 0x03, + .wMaxPacketSize = 10, + .bInterval = 3, + }, }, .on = { .intf = { @@ -116,7 +126,7 @@ static const struct { .bDescriptorType = USB_DT_INTF, .bInterfaceNumber = 0, .bAlternateSetting = 1, - .bNumEndpoints = 3, + .bNumEndpoints = 4, .bInterfaceClass = 0xff, .bInterfaceSubClass = 0xe1, .bInterfaceProtocol = 0x00, @@ -146,6 +156,14 @@ static const struct { .wMaxPacketSize = 8, .bInterval = 3, }, + .ep_interrupt = { + .bLength = sizeof(struct usb_ep_desc), + .bDescriptorType = USB_DT_EP, + .bEndpointAddress = 0x83, + .bmAttributes = 0x03, + .wMaxPacketSize = 10, + .bInterval = 3, + }, }, }, #if 0 diff --git a/firmware/ice40-riscv/icE1usb/usb_e1.c b/firmware/ice40-riscv/icE1usb/usb_e1.c index 0698de5..bbbb24e 100644 --- a/firmware/ice40-riscv/icE1usb/usb_e1.c +++ b/firmware/ice40-riscv/icE1usb/usb_e1.c @@ -25,6 +25,7 @@ struct { int in_bdi; /* buffer descriptor index for IN EP */ struct ice1usb_tx_config tx_cfg; struct ice1usb_rx_config rx_cfg; + struct e1_error_count last_err; } g_usb_e1; /* default configuration at power-up */ @@ -87,6 +88,29 @@ usb_e1_run(void) if (!g_usb_e1.running) return; + /* EP3 IRQ */ + if ((usb_ep_regs[3].in.bd[0].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA) { + const struct e1_error_count *cur_err = e1_get_error_count(); + if (memcmp(cur_err, &g_usb_e1.last_err, sizeof(*cur_err))) { + struct ice1usb_irq errmsg = { + .type = ICE1USB_IRQ_T_ERRCNT, + .u = { + .errors = { + .crc = cur_err->crc, + .align = cur_err->align, + .ovfl = cur_err->ovfl, + .unfl = cur_err->unfl, + .flags = cur_err->flags, + } + } + }; + printf("E"); + usb_data_write(usb_ep_regs[3].in.bd[0].ptr, &errmsg, sizeof(errmsg)); + usb_ep_regs[3].in.bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(sizeof(errmsg)); + g_usb_e1.last_err = *cur_err; + } + } + /* EP2 IN */ bdi = g_usb_e1.in_bdi; @@ -213,6 +237,7 @@ _e1_set_conf(const struct usb_conf_desc *conf) usb_ep_boot(intf, 0x01, true); usb_ep_boot(intf, 0x81, true); usb_ep_boot(intf, 0x82, true); + usb_ep_boot(intf, 0x83, true); return USB_FND_SUCCESS; } @@ -274,6 +299,11 @@ _e1_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel) /* EP1 IN: Queue buffer */ _usb_fill_feedback_ep(); + /* EP3 IN: Interrupt */ + usb_ep_regs[3].in.status = USB_EP_TYPE_INT; + usb_ep_regs[3].in.bd[0].ptr = 68; + usb_ep_regs[3].in.bd[0].csr = 0; + return USB_FND_SUCCESS; }