diff options
Diffstat (limited to 'contrib/firmware/angie/c/src/usb.c')
-rw-r--r-- | contrib/firmware/angie/c/src/usb.c | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/contrib/firmware/angie/c/src/usb.c b/contrib/firmware/angie/c/src/usb.c new file mode 100644 index 0000000..8fd4de6 --- /dev/null +++ b/contrib/firmware/angie/c/src/usb.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/**************************************************************************** + File : usb.c * + Contents : usb communication handling code for NanoXplore USB-JTAG * + ANGIE adapter hardware. * + Based on openULINK project code by: Martin Schmoelzer. * + Copyright 2023, Ahmed Errached BOUDJELIDA, NanoXplore SAS. * + <aboudjelida@nanoxplore.com> * + <ahmederrachedbjld@gmail.com> * +*****************************************************************************/ + +#include "usb.h" +#include "stdint.h" +#include "delay.h" +#include "io.h" +#include "reg_ezusb.h" +#include <fx2macros.h> +#include <serial.h> +#include <stdio.h> + +/* Also update external declarations in "include/usb.h" if making changes to + * these variables! + */ +volatile bool ep1_out; +volatile bool ep1_in; + +volatile __xdata __at 0xE6B8 struct setup_data setup_data; + +/* Define number of endpoints (except Control Endpoint 0) in a central place. + * Be sure to include the necessary endpoint descriptors! + */ +#define NUM_ENDPOINTS 3 + +__code struct usb_device_descriptor device_descriptor = { + .blength = sizeof(struct usb_device_descriptor), + .bdescriptortype = DESCRIPTOR_TYPE_DEVICE, + .bcdusb = 0x0200, /* BCD: 02.00 (Version 2.0 USB spec) */ + .bdeviceclass = 0xFF, /* 0xFF = vendor-specific */ + .bdevicesubclass = 0xFF, + .bdeviceprotocol = 0xFF, + .bmaxpacketsize0 = 64, + .idvendor = 0x584e, + .idproduct = 0x424e, + .bcddevice = 0x0000, + .imanufacturer = 1, + .iproduct = 2, + .iserialnumber = 3, + .bnumconfigurations = 1 +}; + +/* WARNING: ALL config, interface and endpoint descriptors MUST be adjacent! */ + +__code struct usb_config_descriptor config_descriptor = { + .blength = sizeof(struct usb_config_descriptor), + .bdescriptortype = DESCRIPTOR_TYPE_CONFIGURATION, + .wtotallength = sizeof(struct usb_config_descriptor) + + sizeof(struct usb_interface_descriptor) + + (NUM_ENDPOINTS * sizeof(struct usb_endpoint_descriptor)), + .bnuminterfaces = 1, + .bconfigurationvalue = 1, + .iconfiguration = 4, /* String describing this configuration */ + .bmattributes = 0x80, /* Only MSB set according to USB spec */ + .maxpower = 50 /* 100 mA */ +}; + +__code struct usb_interface_descriptor interface_descriptor00 = { + .blength = sizeof(struct usb_interface_descriptor), + .bdescriptortype = DESCRIPTOR_TYPE_INTERFACE, + .binterfacenumber = 0, + .balternatesetting = 0, + .bnumendpoints = NUM_ENDPOINTS, + .binterfaceclass = 0xFF, + .binterfacesubclass = 0xFF, + .binterfaceprotocol = 0xFF, + .iinterface = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep1_out_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (1 | USB_DIR_OUT), + .bmattributes = 0x02, + .wmaxpacketsize = 64, + .binterval = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep1_in_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (1 | USB_DIR_IN), + .bmattributes = 0x02, + .wmaxpacketsize = 64, + .binterval = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep2_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (2 | USB_DIR_OUT), + .bmattributes = 0x02, + .wmaxpacketsize = 512, + .binterval = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep4_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (4 | USB_DIR_IN), + .bmattributes = 0x02, + .wmaxpacketsize = 512, + .binterval = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep6_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (6 | USB_DIR_OUT), + .bmattributes = 0x02, + .wmaxpacketsize = 512, + .binterval = 0 +}; + +__code struct usb_endpoint_descriptor bulk_ep8_endpoint_descriptor = { + .blength = sizeof(struct usb_endpoint_descriptor), + .bdescriptortype = 0x05, + .bendpointaddress = (8 | USB_DIR_OUT), + .bmattributes = 0x02, + .wmaxpacketsize = 512, + .binterval = 0 +}; + +__code struct usb_language_descriptor language_descriptor = { + .blength = 4, + .bdescriptortype = DESCRIPTOR_TYPE_STRING, + .wlangid = {0x0409 /* US English */} +}; + +__code struct usb_string_descriptor strmanufacturer = + STR_DESCR(16, 'N', 'a', 'n', 'o', 'X', 'p', 'l', 'o', 'r', 'e', ',', ' ', 'S', 'A', 'S', '.'); + +__code struct usb_string_descriptor strproduct = + STR_DESCR(13, 'A', 'N', 'G', 'I', 'E', ' ', 'A', 'd', 'a', 'p', 't', 'e', 'r'); + +__code struct usb_string_descriptor strserialnumber = + STR_DESCR(6, '0', '0', '0', '0', '0', '1'); + +__code struct usb_string_descriptor strconfigdescr = + STR_DESCR(12, 'J', 'T', 'A', 'G', ' ', 'A', 'd', 'a', 'p', 't', 'e', 'r'); + +/* Table containing pointers to string descriptors */ +__code struct usb_string_descriptor *__code en_string_descriptors[4] = { + &strmanufacturer, + &strproduct, + &strserialnumber, + &strconfigdescr +}; +void sudav_isr(void)__interrupt SUDAV_ISR +{ + EXIF &= ~0x10; /* Clear USBINT: Main global interrupt */ + USBIRQ = SUDAVI; + EP0CS |= HSNAK; + usb_handle_setup_data(); +} +void sof_isr(void)__interrupt SOF_ISR +{ +} +void sutok_isr(void)__interrupt SUTOK_ISR +{ +} +void suspend_isr(void)__interrupt SUSPEND_ISR +{ +} +void usbreset_isr(void)__interrupt USBRESET_ISR +{ +} +void highspeed_isr(void)__interrupt HIGHSPEED_ISR +{ +} +void ep0ack_isr(void)__interrupt EP0ACK_ISR +{ +} +void stub_isr(void)__interrupt STUB_ISR +{ +} +void ep0in_isr(void)__interrupt EP0IN_ISR +{ +} +void ep0out_isr(void)__interrupt EP0OUT_ISR +{ +} +void ep1in_isr(void)__interrupt EP1IN_ISR +{ + ep1_in = true; + + EXIF &= ~0x10; /* Clear USBINT: Main global interrupt */ + EPIRQ = 0x04; /* Clear individual EP1IN IRQ */ +} +void ep1out_isr(void)__interrupt EP1OUT_ISR +{ + ep1_out = true; + + EXIF &= ~0x10; /* Clear USBINT: Main global interrupt */ + EPIRQ = 0x08; /* Clear individual EP1OUT IRQ */ +} +void ep2_isr(void)__interrupt EP2_ISR +{ + ep1_out = false; /* Does nothing but required by the compiler */ +} +void ep4_isr(void)__interrupt EP4_ISR +{ +} +void ep6_isr(void)__interrupt EP6_ISR +{ +} +void ep8_isr(void)__interrupt EP8_ISR +{ +} +void ibn_isr(void)__interrupt IBN_ISR +{ +} +void ep0pingnak_isr(void)__interrupt EP0PINGNAK_ISR +{ +} +void ep1pingnak_isr(void)__interrupt EP1PINGNAK_ISR +{ +} +void ep2pingnak_isr(void)__interrupt EP2PINGNAK_ISR +{ +} +void ep4pingnak_isr(void)__interrupt EP4PINGNAK_ISR +{ +} +void ep6pingnak_isr(void)__interrupt EP6PINGNAK_ISR +{ +} +void ep8pingnak_isr(void)__interrupt EP8PINGNAK_ISR +{ +} +void errorlimit_isr(void)__interrupt ERRORLIMIT_ISR +{ +} +void ep2piderror_isr(void)__interrupt EP2PIDERROR_ISR +{ +} +void ep4piderror_isr(void)__interrupt EP4PIDERROR_ISR +{ +} +void ep6piderror_isr(void)__interrupt EP6PIDERROR_ISR +{ +} +void ep8piderror_isr(void)__interrupt EP8PIDERROR_ISR +{ +} +void ep2pflag_isr(void)__interrupt EP2PFLAG_ISR +{ +} +void ep4pflag_isr(void)__interrupt EP4PFLAG_ISR +{ +} +void ep6pflag_isr(void)__interrupt EP6PFLAG_ISR +{ +} +void ep8pflag_isr(void)__interrupt EP8PFLAG_ISR +{ +} +void ep2eflag_isr(void)__interrupt EP2EFLAG_ISR +{ +} +void ep4eflag_isr(void)__interrupt EP4EFLAG_ISR +{ +} +void ep6eflag_isr(void)__interrupt EP6EFLAG_ISR +{ +} +void ep8eflag_isr(void)__interrupt EP8EFLAG_ISR +{ +} +void ep2fflag_isr(void)__interrupt EP2FFLAG_ISR +{ +} +void ep4fflag_isr(void)__interrupt EP4FFLAG_ISR +{ +} +void ep6fflag_isr(void)__interrupt EP6FFLAG_ISR +{ +} +void ep8fflag_isr(void)__interrupt EP8FFLAG_ISR +{ +} +void gpifcomplete_isr(void)__interrupt GPIFCOMPLETE_ISR +{ +} +void gpifwaveform_isr(void)__interrupt GPIFWAVEFORM_ISR +{ +} + +/** + * Return the control/status register for an endpoint + * + * @param ep endpoint address + * @return on success: pointer to Control & Status register for endpoint + * specified in \a ep + * @return on failure: NULL + */ +__xdata uint8_t *usb_get_endpoint_cs_reg(uint8_t ep) +{ + /* Mask direction bit */ + uint8_t ep_num = ep & ~0x80; + + switch (ep_num) { + case 0: + return &EP0CS; + case 1: + return ep & 0x80 ? &EP1INCS : &EP1OUTCS; + case 2: + return &EP2CS; + case 4: + return &EP4CS; + case 6: + return &EP6CS; + case 8: + return &EP8CS; + default: + return NULL; + } +} + +void usb_reset_data_toggle(uint8_t ep) +{ + /* TOGCTL register: + +----+-----+-----+------+-----+-------+-------+-------+ + | Q | S | R | IO | EP3 | EP2 | EP1 | EP0 | + +----+-----+-----+------+-----+-------+-------+-------+ + + To reset data toggle bits, we have to write the endpoint direction (IN/OUT) + to the IO bit and the endpoint number to the EP2..EP0 bits. Then, in a + separate write cycle, the R bit needs to be set. + */ + TOGCTL = (((ep & 0x80) >> 3) + (ep & 0x0F)); + TOGCTL |= BMRESETTOGGLE; +} + +/** + * Handle GET_STATUS request. + * + * @return on success: true + * @return on failure: false + */ +bool usb_handle_get_status(void) +{ + uint8_t *ep_cs; + switch (setup_data.bmrequesttype) { + case GS_DEVICE: + /* Two byte response: Byte 0, Bit 0 = self-powered, Bit 1 = remote wakeup. + * Byte 1: reserved, reset to zero */ + EP0BUF[0] = 0; + EP0BUF[1] = 0; + + /* Send response */ + EP0BCH = 0; + syncdelay(3); + EP0BCL = 2; + syncdelay(3); + break; + case GS_INTERFACE: + /* Always return two zero bytes according to USB 1.1 spec, p. 191 */ + EP0BUF[0] = 0; + EP0BUF[1] = 0; + + /* Send response */ + EP0BCH = 0; + syncdelay(3); + EP0BCL = 2; + syncdelay(3); + break; + case GS_ENDPOINT: + /* Get stall bit for endpoint specified in low byte of wIndex */ + ep_cs = usb_get_endpoint_cs_reg(setup_data.windex & 0xff); + + if (*ep_cs & EPSTALL) + EP0BUF[0] = 0x01; + else + EP0BUF[0] = 0x00; + + /* Second byte sent has to be always zero */ + EP0BUF[1] = 0; + + /* Send response */ + EP0BCH = 0; + syncdelay(3); + EP0BCL = 2; + syncdelay(3); + break; + default: + return false; + } + return true; +} + +/** + * Handle CLEAR_FEATURE request. + * + * @return on success: true + * @return on failure: false + */ +bool usb_handle_clear_feature(void) +{ + __xdata uint8_t *ep_cs; + + switch (setup_data.bmrequesttype) { + case CF_DEVICE: + /* Clear remote wakeup not supported: stall EP0 */ + STALL_EP0(); + break; + case CF_ENDPOINT: + if (setup_data.wvalue == 0) { + /* Unstall the endpoint specified in wIndex */ + ep_cs = usb_get_endpoint_cs_reg(setup_data.windex); + if (!ep_cs) + return false; + *ep_cs &= ~EPSTALL; + } else { + /* Unsupported feature, stall EP0 */ + STALL_EP0(); + } + break; + default: + /* Vendor commands... */ + break; + } + return true; +} + +/** + * Handle SET_FEATURE request. + * + * @return on success: true + * @return on failure: false + */ +bool usb_handle_set_feature(void) +{ + __xdata uint8_t *ep_cs; + + switch (setup_data.bmrequesttype) { + case SF_DEVICE: + if (setup_data.wvalue == 2) + return true; + break; + case SF_ENDPOINT: + if (setup_data.wvalue == 0) { + /* Stall the endpoint specified in wIndex */ + ep_cs = usb_get_endpoint_cs_reg(setup_data.windex); + if (!ep_cs) + return false; + *ep_cs |= EPSTALL; + } else { + /* Unsupported endpoint feature */ + return false; + } + break; + default: + /* Vendor commands... */ + break; + } + + return true; +} + +/** + * Handle GET_DESCRIPTOR request. + * + * @return on success: true + * @return on failure: false + */ +bool usb_handle_get_descriptor(void) +{ + __xdata uint8_t descriptor_type; + __xdata uint8_t descriptor_index; + + descriptor_type = (setup_data.wvalue & 0xff00) >> 8; + descriptor_index = setup_data.wvalue & 0x00ff; + + switch (descriptor_type) { + case DESCRIPTOR_TYPE_DEVICE: + SUDPTRH = HI8(&device_descriptor); + SUDPTRL = LO8(&device_descriptor); + break; + case DESCRIPTOR_TYPE_CONFIGURATION: + SUDPTRH = HI8(&config_descriptor); + SUDPTRL = LO8(&config_descriptor); + break; + case DESCRIPTOR_TYPE_STRING: + if (setup_data.windex == 0) { + /* Supply language descriptor */ + SUDPTRH = HI8(&language_descriptor); + SUDPTRL = LO8(&language_descriptor); + } else if (setup_data.windex == 0x0409 /* US English */) { + /* Supply string descriptor */ + SUDPTRH = HI8(en_string_descriptors[descriptor_index - 1]); + SUDPTRL = LO8(en_string_descriptors[descriptor_index - 1]); + } else { + return false; + } + break; + default: + /* Unsupported descriptor type */ + return false; + } + return true; +} + +/** + * Handle SET_INTERFACE request. + */ +void usb_handle_set_interface(void) +{ + /* Reset Data Toggle */ + usb_reset_data_toggle(USB_DIR_IN | 4); + usb_reset_data_toggle(USB_DIR_OUT | 2); + + /* Unstall & clear busy flag of all valid IN endpoints */ + EP1INCS = 0 | EPBSY; + + /* Unstall all valid OUT endpoints, reset bytecounts */ + EP1OUTCS = 0; + EP1OUTBC = 0; + syncdelay(3); +} + +/* Initialize GPIF interface transfer count */ +void set_gpif_cnt(uint32_t count) +{ + GPIFTCB3 = (uint8_t)(((uint32_t)(count) >> 24) & 0x000000ff); + syncdelay(3); + GPIFTCB2 = (uint8_t)(((uint32_t)(count) >> 16) & 0x000000ff); + syncdelay(3); + GPIFTCB1 = (uint8_t)(((uint32_t)(count) >> 8) & 0x000000ff); + syncdelay(3); + GPIFTCB0 = (uint8_t)((uint32_t)(count) & 0x000000ff); +} + +/* + * Vendor commands handling: +*/ +#define VR_CFGOPEN 0xB0 +#define VR_CFGCLOSE 0xB1 + +uint8_t ix; +uint8_t bcnt; +uint8_t __xdata *eptr; +uint16_t wcnt; +uint32_t __xdata gcnt; +bool usb_handle_send_bitstream(void) +{ + eptr = EP0BUF; /* points to EP0BUF 64-byte register */ + wcnt = setup_data.wlength; /* total transfer count */ + + /* Clear EP0BUF for OUT requests */ + if (setup_data.bmrequesttype & 0x80) { + bcnt = ((wcnt > 64) ? 64 : wcnt); + for (ix = 0; ix < bcnt; ix++) + eptr[ix] = 0; + } + + switch (setup_data.brequest) { + case VR_CFGOPEN: + /* Clear bytecount / to allow new data in / to stops NAKing */ + EP0BCH = 0; + EP0BCL = 0; + while (EP0CS & EPBSY) + ; /* wait to finish transferring in EP0BUF, until not busy */ + gcnt = ((uint32_t)(eptr[0]) << 24) | ((uint32_t)(eptr[1]) << 16) + | ((uint32_t)(eptr[2]) << 8) | (uint32_t)(eptr[3]); + /* Angie board FPGA bitstream download */ + switch ((setup_data.wvalue) & 0x00C0) { + case 0x00: + PIN_PROGRAM_B = 0; /* Apply RPGM- pulse */ + GPIFWFSELECT = 0xF2; /* Restore Config mode waveforms select */ + syncdelay(3); + EP2FIFOCFG = BMAUTOOUT; /* and Automatic 8-bit GPIF OUT mode */ + syncdelay(3); + PIN_PROGRAM_B = 1; /* Negate RPGM- pulse */ + delay_ms(10); /* FPGA init time < 10mS */ + set_gpif_cnt(gcnt); /* Initialize GPIF interface transfer count */ + PIN_RDWR_B = 0; + PIN_CSI_B = 0; + GPIFTRIG = GPIF_EP2; /* Trigger GPIF OUT transfer on EP2 */ + syncdelay(3); + break; + default: + break; + } + break; + case VR_CFGCLOSE: + ix = 10; + /* wait until GPIF transaction has been completed */ + while ((GPIFTRIG & BMGPIFDONE) == 0) { + if (ix-- == 0) { + printf("GPIF done time out\n"); + break; + } + delay_ms(1); + } + switch ((setup_data.wvalue) & 0x00C0) { + case 0x00: + PIN_CSI_B = 1; + PIN_RDWR_B = 1; + IFCONFIG &= 0xFC; /* Exit gpif mode */ + break; + default: + break; + } + EP0BCH = 0; + EP0BCL = (uint8_t)(setup_data.wlength); /* Signal buffer is filled */ + break; + default: + return true; /* Error: unknown VR command */ + } + return false; /* no error; command handled OK */ +} + +/** + * Handle the arrival of a USB Control Setup Packet. + */ +void usb_handle_setup_data(void) +{ + switch (setup_data.brequest) { + case GET_STATUS: + if (!usb_handle_get_status()) + STALL_EP0(); + break; + case CLEAR_FEATURE: + if (!usb_handle_clear_feature()) + STALL_EP0(); + break; + case 2: case 4: + /* Reserved values */ + STALL_EP0(); + break; + case SET_FEATURE: + if (!usb_handle_set_feature()) + STALL_EP0(); + break; + case SET_ADDRESS: + /* Handled by USB core */ + break; + case SET_DESCRIPTOR: + /* Set Descriptor not supported. */ + STALL_EP0(); + break; + case GET_DESCRIPTOR: + if (!usb_handle_get_descriptor()) + STALL_EP0(); + break; + case GET_CONFIGURATION: + /* ANGIE has only one configuration, return its index */ + EP0BUF[0] = config_descriptor.bconfigurationvalue; + EP0BCH = 0; + EP0BCL = 1; + syncdelay(3); + break; + case SET_CONFIGURATION: + /* ANGIE has only one configuration -> nothing to do */ + break; + case GET_INTERFACE: + /* ANGIE only has one interface, return its number */ + EP0BUF[0] = interface_descriptor00.binterfacenumber; + EP0BCH = 0; + EP0BCL = 1; + syncdelay(3); + break; + case SET_INTERFACE: + usb_handle_set_interface(); + break; + case SYNCH_FRAME: + /* Isochronous endpoints not used -> nothing to do */ + break; + default: + /* if not Vendor command, Stall EndPoint 0 */ + if (usb_handle_send_bitstream()) + STALL_EP0(); + break; + } +} + +/** + * Handle the initialization of endpoints. + */ +void ep_init(void) +{ + EP1INCFG = 0xA0; + syncdelay(3); + EP1OUTCFG = 0xA0; + syncdelay(3); + EP2CFG = 0xA0; + syncdelay(3); + EP4CFG = 0x00; + syncdelay(3); + EP6CFG = 0x00; + syncdelay(3); + EP8CFG = 0x00; + syncdelay(3); + + /* arm EP1-OUT */ + EP1OUTBC = 0; + syncdelay(3); + EP1OUTBC = 0; + syncdelay(3); + + /* arm EP1-IN */ + EP1INBC = 0; + syncdelay(3); + EP1INBC = 0; + syncdelay(3); + + /* Standard procedure to reset FIFOs */ + FIFORESET = BMNAKALL; /* NAK all transfers during the reset */ + syncdelay(3); + FIFORESET = 0x02; /* reset EP2 FIFO */ + syncdelay(3); + FIFORESET = 0x00; /* deactivate the NAK all */ + syncdelay(3); + EP2FIFOCFG = 0x00; + syncdelay(3); + EP2FIFOCFG = BMAUTOOUT; /* Automatic 8-bit GPIF OUT mode */ + syncdelay(3); +} + +/** + * Interrupt initialization. Configures USB interrupts. + */ +void interrupt_init(void) +{ + /* Enable Interrupts */ + EA = 1; + + /* Enable USB interrupt (EIE register) */ + EUSB = 1; + EICON |= 0x20; + + /* Enable INT 2 & 4 Autovectoring */ + INTSETUP |= (AV2EN | AV4EN); + + /* Enable individual EP1OUT&IN interrupts */ + EPIE |= 0x0C; + + /* Clear individual USB interrupt IRQ */ + EPIRQ = 0x0C; + + /* Enable SUDAV interrupt */ + USBIEN |= SUDAVI; + + /* Clear SUDAV interrupt */ + USBIRQ = SUDAVI; +} + +/** + * Handle the initialization of io ports. + */ +void io_init(void) +{ + /* PORT A */ + PORTACFG = 0x01; /* 0: normal ou 1: alternate function (each bit) */ + OEA = 0xEF; /* all OUT exept INIT_B IN */ + IOA = 0xFF; + PIN_RDWR_B = 1; + PIN_CSI_B = 1; + PIN_PROGRAM_B = 1; + + /* PORT B */ + OEB = 0xEF; /* all OUT exept TDO */ + IOB = 0xFF; + PIN_TRST = 1; + PIN_TMS = 0; + PIN_TCK = 0; + PIN_TDI = 0; + PIN_SRST = 1; + + /* PORT C */ + PORTCCFG = 0x00; /* 0: normal ou 1: alternate function (each bit) */ + OEC = 0xEF; + IOC = 0xFF; +} |