diff --git a/ChangeLog b/ChangeLog index 8f52978..5ab99e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2019-12-27 NIIBE Yutaka + + * mcu/usb-gd32vf103.c: New. + + * mcu/clk_gpio_init-gd32vf103.c (clock_init): Configure USB clock. + + * mcu/gd32vf103.h (RCU_CFG0_USBFSPSC_DIV2): New. + + * usb_lld.h [MCU_GD32VF1]: Support GD32VF103. + 2019-12-27 NIIBE Yutaka * chopstx.c (chopstx_poll): Call CHECK for condition diff --git a/mcu/clk_gpio_init-gd32vf103.c b/mcu/clk_gpio_init-gd32vf103.c index 75422ca..bb74eab 100644 --- a/mcu/clk_gpio_init-gd32vf103.c +++ b/mcu/clk_gpio_init-gd32vf103.c @@ -17,6 +17,8 @@ clock_init (void) RCU->CFG0 |= RCU_CFG0_APB2_CKAHB_DIV1; /* CK_APB1 = CK_AHB/2 */ RCU->CFG0 |= RCU_CFG0_APB1_CKAHB_DIV2; + /* CK_USBFS = 48MHz */ + RCU->CFG0 |= RCU_CFG0_USBFSPSC_DIV2; /* CK_ADC, CK_TIMER1xxx, CK_TIMER0, CK_I2S */ diff --git a/mcu/gd32vf103.h b/mcu/gd32vf103.h index 4e10c01..4ae585f 100644 --- a/mcu/gd32vf103.h +++ b/mcu/gd32vf103.h @@ -33,6 +33,7 @@ static struct RCU *const RCU = (struct RCU *)0x40021000; #define RCU_CFG0_SCS_MASK 0x00000003 #define RCU_CFG0_SCSS_PLL 0x00000008 #define RCU_CFG0_CKSYSSRC_PLL 0x00000002 +#define RCU_CFG0_USBFSPSC_DIV2 0x00c00000 #define RCU_CFG1_PREDV0SEL_MASK 0x00010000 #define RCU_CFG1_PREDV0SEL_HXTAL 0x00000000 @@ -41,6 +42,8 @@ static struct RCU *const RCU = (struct RCU *)0x40021000; #define RCU_APB2_GPIOB 0x00000008 #define RCU_APB2_GPIOC 0x00000010 +#define RCU_AHB_USBFS 0x00001000 + /* Semantics is exactly same as STM32F103. */ struct GPIO { volatile uint32_t CRL; diff --git a/mcu/usb-gd32vf103.c b/mcu/usb-gd32vf103.c new file mode 100644 index 0000000..de28345 --- /dev/null +++ b/mcu/usb-gd32vf103.c @@ -0,0 +1,1268 @@ +/* + * usb-gd32vf103.c - USB driver for GD32VF103 + * + * Copyright (C) 2019 Flying Stone Technology + * Author: NIIBE Yutaka + * + * This file is a part of Chopstx, a thread library for embedded. + * + * Chopstx is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chopstx is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * As additional permission under GNU GPL version 3 section 7, you may + * distribute non-source form of the Program without the copy of the + * GNU GPL normally required by section 4, provided you inform the + * recipients of GNU GPL by a written offer. + * + */ + +#include +#include +#include + +#include +#include + +#include "usb_lld.h" + +/* + * This driver is for USB device functionality (not host). + * + * This driver doesn't support isochronous transfer (yet). + * Only supports one packet at a time. + */ + +#ifdef DEBUG_BY_SERIAL_OUTPUT +extern void debug (const char *fmt, ...); +#else +#define debug(a,...) /**/ +#endif + +#define USB_DIR_OUT 0 +#define USB_DIR_IN 1 +#define USB_DIR_BOTH 2 + +#define USBFS_BASE 0x50000000 + +/* USB Global registers */ +struct USB_G { + volatile uint32_t GOTGCS; + volatile uint32_t GOTGINTF; + volatile uint32_t GAHBCS; + volatile uint32_t GUSBCS; + + volatile uint32_t GRSTCTL; + volatile uint32_t GINTF; + volatile uint32_t GINTEN; + volatile uint32_t GRSTATR; + + volatile uint32_t GRSTATP; + volatile uint32_t GRFLEN; + volatile uint32_t DIEP0TFLEN_HNPTFLEN; + volatile uint32_t HNPTFQSTAT; + + uint32_t rsvd0; + uint32_t rsvd1; + volatile uint32_t GCCFG; + volatile uint32_t CID; + uint32_t rsvd2[48]; + uint32_t rsvd3; + volatile uint32_t DIEP_TFLEN[3]; +}; +static struct USB_G *const USB_G = (struct USB_G *)USBFS_BASE; + +/* USB Device registers */ +struct USB_D { + volatile uint32_t DCFG; + volatile uint32_t DCTL; + volatile uint32_t DSTAT; + uint32_t rsvd0; + + volatile uint32_t DIEPINTEN; + volatile uint32_t DOEPINTEN; + volatile uint32_t DAEPINT; + volatile uint32_t DAEPINTEN; + + uint32_t rsvd1; + uint32_t rsvd2; + volatile uint32_t DVBUSDT; + volatile uint32_t DVPUSPT; + + uint32_t rsvd3; + volatile uint32_t DIEPFEINTEN; +}; +static struct USB_D *const USB_D = (struct USB_D *)(USBFS_BASE+0x800); + +/* USB device endpoint IN registers */ +struct USB_I { + volatile uint32_t DIEP_CTL; + uint32_t rsvd0; + volatile uint32_t DIEP_INTF; + uint32_t rsvd1; + + volatile uint32_t DIEP_LEN; + uint32_t rsvd2; + volatile uint32_t DIEP_TFSTAT; + uint32_t rsvd3; +}; +static struct USB_I *const USB_I = (struct USB_I *)(USBFS_BASE+0x900); + +/* USB device endpoint OUT registers */ +struct USB_O { + volatile uint32_t DOEP_CTL; + uint32_t rsvd0; + volatile uint32_t DOEP_INTF; + uint32_t rsvd1; + + volatile uint32_t DOEP_LEN; + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t rsvd4; +}; +static struct USB_O *const USB_O = (struct USB_O *)(USBFS_BASE+0xB00); + +#if 0 +/* USB Power control register */ +struct USB_P { + volatile uint32_t PWRCLKCTL; +}; +static struct USB_P *const USB_P = (struct USB_P *)(USBFS_BASE+0xE00); +#endif + +struct USB_FIFO { + struct { + volatile uint32_t data; + uint32_t rsvd[1023]; + } FIFO[4]; +}; +static struct USB_FIFO *const USB_FIFO = (struct USB_FIFO *)(USBFS_BASE+0x1000); + +#define USB_ENDPOINT_MAX 4 + +static void +gd32vf_usb_clock_init (void) +{ + /* Reset the clock. */ + if ((RCU->AHBEN & RCU_AHB_USBFS) + && (RCU->AHBRST & RCU_AHB_USBFS) == 0) + /* Make sure the device is disconnected, even after core reset. */ + { + RCU->AHBEN &= ~RCU_AHB_USBFS; + RCU->AHBRST = RCU_AHB_USBFS; + + /* Disconnect requires SE0 (>= 2.5uS). */ + chopstx_usec_wait (10); + } + + if ((RCU->AHBEN & RCU_AHB_USBFS) == 0) + { + RCU->AHBEN |= RCU_AHB_USBFS; + RCU->AHBRST = RCU_AHB_USBFS; + RCU->AHBRST = 0; + } +} + +static void +gd32vf_usb_init (void) +{ + uint32_t usb_ramaddr = 0; + int i; + + gd32vf_usb_clock_init (); + + /* Undocumented setting of PHY (GD specific). */ + USB_G->GUSBCS |= (1 << 6); /* Use embedded PHY */ + + /* Enable the transceiver. */ + USB_G->GCCFG = (1 << 19) | (1 << 18) | (1 << 16); + /* Disable monitoring VBUS pin (GD specific). */ + USB_G->GCCFG |= (1 << 21); + + chopstx_usec_wait (20*1000); + + /* Disconnect. */ + USB_D->DCTL |= (1 << 1); + + /* Select device mode (not host mode or OTG). */ + USB_G->GUSBCS &= ~((1 << 30) | (1 << 29)); + USB_G->GUSBCS |= (1 << 30); + + chopstx_usec_wait (10*1000); + + /* Configure the receive FIFO RAM address */ + USB_G->GRFLEN = 64; + usb_ramaddr += 64; + + USB_G->DIEP0TFLEN_HNPTFLEN = (64 << 16) | usb_ramaddr; + usb_ramaddr += 64; + + for (i = 1; i < USB_ENDPOINT_MAX; i++) + { + USB_G->DIEP_TFLEN[i - 1] = (64 << 16) | usb_ramaddr; + usb_ramaddr += 64; + } + + /* Configure device mode. */ + USB_D->DCFG &= ~((1 << 12) | (1 << 11)); /* 80% of frame interval */ + USB_D->DCFG |= ((1 << 1)| (1 << 0)); /* Full speed */ + + /* Connect */ + USB_D->DCTL &= ~(1 << 1); + + /* Interrupt setup. */ + + /* Transfer finished. */ + USB_D->DIEPINTEN |= (1 << 0); + /* Setup finished, Transfer finished. */ + USB_D->DOEPINTEN |= (1 << 3) | (1 << 0); + /* Configure for endpoint 0, IN and OUT. */ + USB_D->DAEPINTEN = (1 << 16) | (1 << 0); + + /* Wakeup and Suspend. */ + USB_G->GINTEN |= (1 << 31) | (1 << 11); + /* IN and OUT. */ + USB_G->GINTEN |= (1 << 19) | (1 << 18); + /* USB Reset. */ + USB_G->GINTEN |= (1 << 12); + /* Speed enumeration. */ + USB_G->GINTEN |= (1 << 13); + + /* RxFIFO non-empty */ + USB_G->GINTEN |= (1 << 4); + + /* Note: We don't enable event for start of frame (1 << 3) */ + + /* TxFIFO completely empty, Enable interrupt from the USB module. */ + USB_G->GAHBCS |= (1 << 7) | (1 << 0); +} + +static void +gd32vf_set_daddr (uint8_t daddr) +{ + USB_D->DCFG = (USB_D->DCFG & ~(0x7f << 4)) | (daddr << 4); +} + +static void +gd32vf_prepare_ep0_setup_init (void) +{ + /* Max packet length: 64-byte. */ + USB_I[0].DIEP_CTL &= ~0x03; + + /* Clear global OUT NAK, IN NAK. */ + USB_D->DCTL |= (1 << 10) | (1 << 8); +} + +static void +gd32vf_prepare_ep0_setup (struct usb_dev *dev) +{ + dev->ctrl_data.addr = (uint8_t *)&dev->dev_req; + dev->ctrl_data.len = 8; + dev->ctrl_data.require_zlp = 0; + + /* Configure control endpoint to accept 8-byte SETUP stage. */ + /* + * Note: It makes no sense to configure back-to-back setup. + * In a correct control transfer, a setup stage is always + * followed by data stage or status stage. + */ + /* setup_cnt=1, packet_cnt=1, buflen=8-byte. */ + USB_O[0].DOEP_LEN = (1 << 29) | (1 << 19) | 8; + USB_O[0].DOEP_CTL |= (1 << 31) | (1 << 26); /* Enable and Clear NAK */ +} + +static void +gd32vf_tx_fifo_flush (int n) +{ + USB_G->GRSTCTL = (n << 6) | (1 << 5); + while ((USB_G->GRSTCTL & (1 << 5))) + chopstx_usec_wait (100); + chopstx_usec_wait (100); +} + +static void +gd32vf_tx_fifo_write (int n, const void *buf, uint8_t len) +{ + const uint8_t *p = buf; + uint32_t data; + int i; + +#if 0 + if ((USB_I[n].DIEP_TFSTAT & 0xffff) < len) + { /* Not enough space, yet */ + return; + } +#endif + + for (i = 0; i < len / 4; i++) + { + data = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + p += 4; + USB_FIFO->FIFO[n].data = data; + } + + if ((len % 4)) + { + data = 0; + for (i = 0; i < (len % 4); i++) + data |= (*p++) << (i * 8); + USB_FIFO->FIFO[n].data = data; + } +} + +static void +gd32vf_prepare_ep0_in (uint8_t len) +{ + USB_I[0].DIEP_LEN = (1 << 19) | len; /* 1-packet, len-byte */ + if (len) + USB_D->DIEPFEINTEN |= (1 << 0); + USB_I[0].DIEP_CTL |= (1 << 31) | (1 << 26); /* Enable and Clear NAK */ +} + +/* Note: No support of multiple DATA transfer. It's by DATA1 only. */ +static void +gd32vf_prepare_ep0_out (uint8_t len) +{ + /* Set pktcnt=1 and length, while keeping SETUP_COUNT field. */ + USB_O[0].DOEP_LEN &= ~127; /* Clear length field. */ + USB_O[0].DOEP_LEN |= (1 << 19) | len; + USB_O[0].DOEP_CTL = (1 << 31) | (1 << 26); /* Enable and Clear NAK */ +} + +static int +gd32vf_ep_is_active (uint8_t n, int dir) +{ + uint32_t bit_to_examine; + + if (dir == USB_DIR_IN) + bit_to_examine = (USB_I[n].DIEP_CTL & (1 << 15)); + else + bit_to_examine = (USB_O[n].DOEP_CTL & (1 << 15)); + + return bit_to_examine != 0; +} + +static int +gd32vf_ep_is_stalled (uint8_t n, int dir) +{ + uint32_t bit_to_examine; + + if (dir == USB_DIR_IN) + bit_to_examine = (USB_I[n].DIEP_CTL & (1 << 21)); + else + bit_to_examine = (USB_O[n].DOEP_CTL & (1 << 21)); + + return bit_to_examine != 0; +} + +static void +gd32vf_ep_stall (uint8_t n, int dir) +{ + if (dir == USB_DIR_IN || dir == USB_DIR_BOTH) + { + /* When it's active, disable it. */ + if (USB_I[n].DIEP_CTL & (1 << 31)) + USB_I[n].DIEP_CTL |= (1 << 30); + + USB_I[n].DIEP_CTL |= (1 << 21); + } + + if (dir == USB_DIR_OUT || dir == USB_DIR_BOTH) + USB_O[n].DOEP_CTL |= (1 << 21); +} + +static void +gd32vf_ep_clear_stall (uint8_t n, int dir) +{ + if (dir == USB_DIR_IN || dir == USB_DIR_BOTH) + { + USB_I[n].DIEP_CTL &= ~(1 << 21); + USB_I[n].DIEP_CTL |= (1 << 28); + } + + if (dir == USB_DIR_OUT || dir == USB_DIR_BOTH) + { + USB_O[n].DOEP_CTL &= ~(1 << 21); + USB_O[n].DOEP_CTL |= (1 << 28); + } +} + +#include "usb_lld_driver.h" + +static int handle_in0 (struct usb_dev *dev); +static void handle_out0 (struct usb_dev *dev); +static int handle_setup0 (struct usb_dev *dev); + + +void +usb_lld_stall_tx (int n) +{ + gd32vf_ep_stall (n, USB_DIR_IN); +} + +void +usb_lld_stall_rx (int n) +{ + gd32vf_ep_stall (n, USB_DIR_OUT); +} + + +void +usb_lld_ctrl_error (struct usb_dev *dev) +{ + dev->state = STALLED; + gd32vf_ep_stall (ENDP0, USB_DIR_BOTH); +} + +int +usb_lld_ctrl_ack (struct usb_dev *dev) +{ + debug ("C W ack"); + /* Zero length packet for ACK. */ + dev->state = WAIT_STATUS_IN; + gd32vf_prepare_ep0_in (0); + return USB_EVENT_OK; +} + + +void +usb_lld_init (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + dev->state = WAIT_SETUP; + gd32vf_usb_init (); +} + + +static void +read_from_rx_fifo (struct usb_dev *dev) +{ + /* RxFIFO ready. */ + uint32_t stat = USB_G->GRSTATP; + uint8_t ep_num = (stat & 0x0f); + uint16_t len = (stat >> 4) & 0x7ff; + uint8_t pkt = (stat >> 17) & 0x0f; + + if (ep_num == 0) + { + uint8_t *p = dev->ctrl_data.addr; + uint32_t data; + int i; + + if (pkt == 0x06) + { + debug ("\rrx S %d", len); + if (dev->ctrl_data.len == 0) + { + /* Receiving re-send of SETUP packet. */ + p = dev->ctrl_data.addr - 8; + } + } + else if (pkt == 0x02) + { + if (len > dev->ctrl_data.len) + { + len = dev->ctrl_data.len; + debug ("rx O %d*", len); + } + else + { + debug ("rx O %d", len); + } + } + else + return; + + for (i = 0; i < len / 4; i++) + { +#ifdef DEBUG_BY_SERIAL_OUTPUT + uint8_t *p0 = p; +#endif + data = USB_FIFO->FIFO[0].data; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + debug ("%02x %02x %02x %02x", p0[0], p0[1], p0[2], p0[3]); + } + + if ((len % 4)) + { + data = USB_FIFO->FIFO[0].data; + for (i = 0; i < (len % 4); i++) + { + *p++ = (data & 0xff); + debug ("%02x", (data & 0xff)); + data >>= 8; + } + } + + dev->ctrl_data.len -= len; + dev->ctrl_data.addr += len; + } + else + { + uint8_t *p = dev->epctl_rx[ep_num-1].addr; + uint32_t data; + int i; + + if (pkt == 0x02) + { + if (len > dev->epctl_rx[ep_num-1].len) + { + len = dev->epctl_rx[ep_num-1].len; + debug ("\rrx EP%d O %d*", ep_num, len); + } + else + { + debug ("\rrx EP%d O %d", ep_num, len); + } + + dev->epctl_rx[ep_num-1].len = len; + } + else + return; + + for (i = 0; i < len / 4; i++) + { +#ifdef DEBUG_BY_SERIAL_OUTPUT + uint8_t *p0 = p; +#endif + data = USB_FIFO->FIFO[ep_num].data; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + data >>= 8; + *p++ = (data & 0xff); + debug ("%02x %02x %02x %02x", p0[0], p0[1], p0[2], p0[3]); + } + + if ((len % 4)) + { + data = USB_FIFO->FIFO[ep_num].data; + for (i = 0; i < (len % 4); i++) + { + *p++ = (data & 0xff); + debug ("%02x", (data & 0xff)); + data >>= 8; + } + } + } +} + +#define USB_MAKE_EV(event) (event<<24) +#define USB_MAKE_TXRX(ep_num,txrx,len) ((txrx? (1<<23):0)|(ep_num<<16)|len) + +int +usb_lld_event_handler (struct usb_dev *dev) +{ + uint32_t gintr = (USB_G->GINTF & USB_G->GINTEN); + uint32_t bits; + uint32_t ep_num = 0; + static uint32_t sof = 0; + + if (!gintr) + return USB_EVENT_OK; + else if ((gintr & (1 << 3))) + { + /* sof */ + if ((sof++ & 0xff) == 0xff) + { + debug ("sof"); + } + USB_G->GINTF = (1 << 3); + return USB_EVENT_OK; + } + else if ((gintr & (1 << 31))) + { + /* wakeup */ + debug ("\rwakeup"); + USB_G->GINTF = (1 << 31); + return USB_MAKE_EV (USB_EVENT_DEVICE_WAKEUP); + } + else if ((gintr & (1 << 11))) + { + debug ("\rsuspend"); + /* suspend */ + USB_G->GINTF = (1 << 11); + return USB_MAKE_EV (USB_EVENT_DEVICE_SUSPEND); + } + else if ((gintr & (1 << 19))) + { + /* SETUP or OUT */ + for (bits = ((USB_D->DAEPINT >> 16) & 0x0f); bits; bits >>= 1, ep_num++) + if ((bits & 1)) + { +#if 0 + if ((USB_O[ep_num].DOEP_INTF & (1 << 6))) + { + /* Multiple SETUP packets received. */ + USB_O[ep_num].DOEP_INTF |= (1 << 6); + } +#endif + + if (ep_num == 0 && (USB_O[ep_num].DOEP_INTF & (1 << 3))) + { + int r = USB_MAKE_EV (handle_setup0 (dev)); + USB_O[ep_num].DOEP_INTF |= (1 << 3); + return r; + } + else if ((USB_O[ep_num].DOEP_INTF & (1 << 0))) + { + USB_O[ep_num].DOEP_INTF |= (1 << 0); + debug ("O %d", ep_num); + if (ep_num == 0) + { + /* Ignore transfer finished for OUT which comes at SETUP */ + if (dev->state != WAIT_SETUP) + handle_out0 (dev); + return USB_EVENT_OK; + } + else + return USB_MAKE_TXRX (ep_num, 0, dev->epctl_rx[ep_num-1].len); + } + } + } + else if ((gintr & (1 << 18))) + { + /* IN */ + for (bits = (USB_D->DAEPINT & 0x0f); bits; bits >>= 1, ep_num++) + if ((bits & 1)) + { +#if 0 + if ((USB_I[ep_num].DIEP_INTF & (1 << 3))) + { + USB_I[ep_num].DIEP_INTF = (1 << 3); + } +#endif + + if ((USB_I[ep_num].DIEP_INTF & (1 << 0))) + { + USB_I[ep_num].DIEP_INTF = (1 << 0); + debug ("I %d", ep_num); + if (ep_num == 0) + return USB_MAKE_EV (handle_in0 (dev)); + else + return USB_MAKE_TXRX (ep_num, 1, dev->epctl_tx[ep_num-1].len); + } + + if ((USB_I[ep_num].DIEP_INTF & (1 << 7))) + { + uint8_t len; + + len = USB_I[ep_num].DIEP_LEN & 0x7f; + debug ("fifo tx %d (%d)", ep_num, len); + if (ep_num) + { + gd32vf_tx_fifo_write (ep_num, dev->epctl_tx[ep_num-1].addr, + len); + USB_D->DIEPFEINTEN &= ~(1 << ep_num); + } + else + { + gd32vf_tx_fifo_write (0, dev->ctrl_data.addr, len); + + dev->ctrl_data.len -= len; + dev->ctrl_data.addr += len; + if (dev->ctrl_data.len == 0) + { + USB_D->DIEPFEINTEN &= ~(1 << ep_num); + debug ("< %d: done", len); + } + else + { + debug ("< %d", len); + } + } + + USB_I[ep_num].DIEP_INTF = (1 << 7); + } + } + } + else if ((gintr & (1 << 4))) + { + read_from_rx_fifo (dev); + } + else if ((gintr & (1 << 13))) + { + /* Speed enumeration: HS/FS negotiation. */ + /* + * Set the UTT (USB turnaround time). + * Value is not documented for GD. + * Need change for STM32. + */ + USB_G->GUSBCS |= 0x05 << 10; + USB_G->GINTF = (1 << 13); + debug ("spd=FS"); + } + else if ((gintr & (1 << 12))) + { + /* USB RESET */ + USB_G->GINTF = (1 << 12); + return USB_MAKE_EV (USB_EVENT_DEVICE_RESET); + } + + return USB_EVENT_OK; +} + + +static void +handle_datastage_out (struct usb_dev *dev) +{ + if (dev->ctrl_data.len == 0) + { + debug ("C W ack!"); + dev->state = WAIT_STATUS_IN; + gd32vf_prepare_ep0_in (0); + } + else + { + uint8_t len = dev->ctrl_data.len; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + + dev->state = OUT_DATA; + gd32vf_prepare_ep0_out (len); + } +} + + +static void +handle_datastage_in (struct usb_dev *dev) +{ + uint32_t len = USB_MAX_PACKET_SIZE; + struct ctrl_data *data_p = &dev->ctrl_data; + + if ((data_p->len == 0) && (dev->state == LAST_IN_DATA)) + { + if (data_p->require_zlp) + { + data_p->require_zlp = 0; + + /* No more data to send. Send empty packet */ + gd32vf_prepare_ep0_in (0); + } + else + { + /* No more data to send, proceed to receive OUT acknowledge. */ + dev->state = WAIT_STATUS_OUT; + gd32vf_prepare_ep0_out (0); + } + + return; + } + + dev->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA; + + if (len > data_p->len) + len = data_p->len; + + gd32vf_prepare_ep0_in (len); +} + +typedef int (*HANDLER) (struct usb_dev *dev); + +static int +std_none (struct usb_dev *dev) +{ + (void)dev; + return -1; +} + +static uint16_t status_info; + +static int +direction_of_endpoint (struct device_req *arg) +{ + return (arg->index & 0x80)? USB_DIR_IN : USB_DIR_OUT; +} + +static int +std_get_status (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0 + || USB_SETUP_SET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->index == 0) + { + /* Get Device Status */ + uint8_t feature = dev->feature; + + /* Remote Wakeup enabled */ + if ((feature & (1 << 5))) + status_info |= 2; + else + status_info &= ~2; + + /* Bus-powered */ + if ((feature & (1 << 6))) + status_info |= 1; + else /* Self-powered */ + status_info &= ~1; + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + } + else if (rcp == INTERFACE_RECIPIENT) + { + if (dev->configuration == 0) + return -1; + + return USB_EVENT_GET_STATUS_INTERFACE; + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if ((arg->index & 0x70) || n == ENDP0) + return -1; + + if (!gd32vf_ep_is_active (n, direction_of_endpoint (arg))) + return -1; + + status_info = gd32vf_ep_is_stalled (n, direction_of_endpoint (arg)); + + return usb_lld_ctrl_send (dev, &status_info, 2); + } + + return -1; +} + +static int +std_clear_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature &= ~(1 << 5); + return USB_EVENT_CLEAR_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || n == ENDP0) + return -1; + + if (!gd32vf_ep_is_active (n, direction_of_endpoint (arg))) + return -1; + + gd32vf_ep_clear_stall (n, direction_of_endpoint (arg)); + + return USB_EVENT_CLEAR_FEATURE_ENDPOINT; + } + + return -1; +} + +static int +std_set_feature (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT) + { + if (arg->len != 0 || arg->index != 0) + return -1; + + if (arg->value == FEATURE_DEVICE_REMOTE_WAKEUP) + { + dev->feature |= 1 << 5; + return USB_EVENT_SET_FEATURE_DEVICE; + } + } + else if (rcp == ENDPOINT_RECIPIENT) + { + uint8_t n = (arg->index & 0x0f); + + if (dev->configuration == 0) + return -1; + + if (arg->len != 0 || (arg->index >> 8) != 0 + || arg->value != FEATURE_ENDPOINT_HALT || n == ENDP0) + return -1; + + if (!gd32vf_ep_is_active (n, direction_of_endpoint (arg))) + return -1; + + if ((arg->index & 0x80)) /* IN endpoint */ + gd32vf_ep_stall (n, USB_DIR_IN); + else + gd32vf_ep_stall (n, USB_DIR_OUT); + + return USB_EVENT_SET_FEATURE_ENDPOINT; + } + + return -1; +} + +static int +std_set_address (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127 + && arg->index == 0 && dev->configuration == 0) + { + gd32vf_set_daddr (arg->value); + return usb_lld_ctrl_ack (dev); + } + + return -1; +} + +static int +std_get_descriptor (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + if (USB_SETUP_SET (arg->type)) + return -1; + + return USB_EVENT_GET_DESCRIPTOR; +} + +static int +std_get_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || arg->index != 0 || arg->len != 1) + return -1; + + if (rcp == DEVICE_RECIPIENT) + return usb_lld_ctrl_send (dev, &dev->configuration, 1); + + return -1; +} + +static int +std_set_configuration (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type)) + return -1; + + if (rcp == DEVICE_RECIPIENT && arg->index == 0 && arg->len == 0) + return USB_EVENT_SET_CONFIGURATION; + + return -1; +} + +static int +std_get_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_SET (arg->type)) + return -1; + + if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1) + return -1; + + if (dev->configuration == 0) + return -1; + + if (rcp == INTERFACE_RECIPIENT) + return USB_EVENT_GET_INTERFACE; + + return -1; +} + +static int +std_set_interface (struct usb_dev *dev) +{ + struct device_req *arg = &dev->dev_req; + uint8_t rcp = arg->type & RECIPIENT; + + if (USB_SETUP_GET (arg->type) || rcp != INTERFACE_RECIPIENT + || arg->len != 0 || (arg->index >> 8) != 0 + || (arg->value >> 8) != 0 || dev->configuration == 0) + return -1; + + return USB_EVENT_SET_INTERFACE; +} + +static int +handle_setup0 (struct usb_dev *dev) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + int r = -1; + HANDLER handler; + + debug ("S"); + data_p->addr = NULL; + data_p->len = 0; + data_p->require_zlp = 0; + + if ((dev->dev_req.type & REQUEST_TYPE) == STANDARD_REQUEST) + { + switch (dev->dev_req.request) + { + case 0: handler = std_get_status; break; + case 1: handler = std_clear_feature; break; + case 3: handler = std_set_feature; break; + case 5: handler = std_set_address; break; + case 6: handler = std_get_descriptor; break; + case 8: handler = std_get_configuration; break; + case 9: handler = std_set_configuration; break; + case 10: handler = std_get_interface; break; + case 11: handler = std_set_interface; break; + default: handler = std_none; break; + } + + if ((r = (*handler) (dev)) < 0) + { + usb_lld_ctrl_error (dev); + return USB_EVENT_OK; + } + else + return r; + } + else + return USB_EVENT_CTRL_REQUEST; +} + +static int +handle_in0 (struct usb_dev *dev) +{ + int r = 0; + + if (dev->state == IN_DATA || dev->state == LAST_IN_DATA) + handle_datastage_in (dev); + else if (dev->state == WAIT_STATUS_IN) + { /* Control WRITE transfer done successfully. */ + if ((dev->dev_req.request == SET_ADDRESS) && + ((dev->dev_req.type & (REQUEST_TYPE | RECIPIENT)) + == (STANDARD_REQUEST | DEVICE_RECIPIENT))) + r = USB_EVENT_DEVICE_ADDRESSED; + else + r = USB_EVENT_CTRL_WRITE_FINISH; + dev->state = WAIT_SETUP; + gd32vf_prepare_ep0_setup (dev); + } + else + { + dev->state = STALLED; + gd32vf_ep_stall (ENDP0, USB_DIR_BOTH); + } + + return r; +} + +static void +handle_out0 (struct usb_dev *dev) +{ + if (dev->state == OUT_DATA) + /* It's normal control WRITE transfer. */ + handle_datastage_out (dev); + else if (dev->state == WAIT_STATUS_OUT) + { /* Control READ transfer done successfully. */ + debug ("C R ack"); + dev->state = WAIT_SETUP; + gd32vf_prepare_ep0_setup (dev); + } + else + { + debug ("."); + /* + * dev->state == IN_DATA || dev->state == LAST_IN_DATA + * (Host aborts the transfer before finish) + * Or else, unexpected state. + * STALL the endpoint, until we receive the next SETUP token. + */ + dev->state = STALLED; + gd32vf_ep_stall (ENDP0, USB_DIR_BOTH); + } +} + +void +usb_lld_reset (struct usb_dev *dev, uint8_t feature) +{ + usb_lld_set_configuration (dev, 0); + dev->feature = feature; + dev->state = WAIT_SETUP; + + USB_D->DCTL &= ~(1 << 0); /* Clear wakeup. */ + + /* This TX FIFO flush is required. With no Tx FIFO flush, writing + about 300 times, it starts erroneous bytes on wire. */ + gd32vf_tx_fifo_flush (0); + + gd32vf_set_daddr (0); + gd32vf_prepare_ep0_setup_init (); + gd32vf_prepare_ep0_setup (dev); +} + +void +usb_lld_setup_endp (struct usb_dev *dev, int n, int ep_type, + int rx_en, int tx_en) +{ + (void)dev; + if (rx_en) + { + if ((USB_O[n].DOEP_CTL & (1 << 15)) == 0) + /* Select DATA0 and EP_TYPE. */ + USB_O[n].DOEP_CTL |= ((1 << 28) | (ep_type << 18) + | (1 << 27) | (1 << 15) | 64); + + USB_D->DAEPINTEN |= (1 << (16 + n)); + } + + if (tx_en) + { + gd32vf_tx_fifo_flush (n); + + if ((USB_I[n].DIEP_CTL & (1 << 15)) == 0) + { + /* Select DATA0, TX FIFO number, and EP_TYPE. */ + /* Initially, no data to send, respond with NAK. + Activate the endpoint. */ + USB_I[n].DIEP_CTL = ((1 << 28) | (n << 22) | (ep_type << 18) + | (1 << 27) | (1 << 15) | 64); + } + + USB_D->DAEPINTEN |= (1 << n); + } +} + + +void +usb_lld_set_configuration (struct usb_dev *dev, uint8_t config) +{ + dev->configuration = config; +} + +uint8_t +usb_lld_current_configuration (struct usb_dev *dev) +{ + return dev->configuration; +} + +int +usb_lld_ctrl_recv (struct usb_dev *dev, void *p, size_t len) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + data_p->addr = (uint8_t *)p; + data_p->len = len; + dev->state = OUT_DATA; + if (len > USB_MAX_PACKET_SIZE) + len = USB_MAX_PACKET_SIZE; + gd32vf_prepare_ep0_out (len); + return USB_EVENT_OK; +} + +/* + * BUF: Pointer to data memory. + * + * BUFLEN: size of the data. + */ +int +usb_lld_ctrl_send (struct usb_dev *dev, const void *buf, size_t buflen) +{ + struct ctrl_data *data_p = &dev->ctrl_data; + uint32_t len_asked = dev->dev_req.len; + uint32_t len; + + debug ("

addr = (void *)buf; + data_p->len = buflen; + + /* Restrict the data length to be the one host asks for */ + if (data_p->len >= len_asked) + data_p->len = len_asked; + /* ZLP is only required when host doesn't expect the end of packets. */ + else if (data_p->len != 0 && (data_p->len % USB_MAX_PACKET_SIZE) == 0) + data_p->require_zlp = 1; + + if (data_p->len <= USB_MAX_PACKET_SIZE) + { + len = data_p->len; + dev->state = LAST_IN_DATA; + } + else + { + len = USB_MAX_PACKET_SIZE; + dev->state = IN_DATA; + } + + gd32vf_prepare_ep0_in (len); + + return USB_EVENT_OK; +} + +void +usb_lld_rx_enable_buf (struct usb_dev *dev, int n, void *buf, size_t len) +{ + dev->epctl_rx[n-1].addr = (uint8_t *)buf; + dev->epctl_rx[n-1].len = (uint16_t)len; + + debug ("\rrx %d (%d)", n, len); + USB_O[n].DOEP_LEN = (1 << 19) | len; /* 1-packet, len-byte */ + USB_O[n].DOEP_CTL |= (1 << 31) | (1 << 26); /* Enable and Clear NAK */ +} + + +void +usb_lld_tx_enable_buf (struct usb_dev *dev, int n, const void *buf, size_t len) +{ + dev->epctl_tx[n-1].addr = (uint8_t *)buf; + dev->epctl_tx[n-1].len = (uint16_t)len; + + debug ("\rtx %d (%d)", n, len); + USB_I[n].DIEP_LEN = (1 << 19) | len; /* 1-packet, len-byte */ + if (len) + USB_D->DIEPFEINTEN |= (1 << n); + USB_I[n].DIEP_CTL |= (1 << 31) | (1 << 26); /* Enable and Clear NAK */ +} diff --git a/usb_lld.h b/usb_lld.h index 7a7961e..613c650 100644 --- a/usb_lld.h +++ b/usb_lld.h @@ -38,11 +38,19 @@ enum DESCRIPTOR_TYPE #define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0) struct device_req { - uint8_t type; - uint8_t request; - uint16_t value; - uint16_t index; - uint16_t len; + union { + struct { + uint8_t type; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t len; + }; + struct { + uint32_t w0; + uint32_t w1; + }; + }; }; struct ctrl_data { @@ -51,12 +59,27 @@ struct ctrl_data { uint8_t require_zlp; }; +#ifdef MCU_GD32VF1 +#define USB_REQUIRE_TXRX_INFO 1 +#endif + +#ifdef USB_REQUIRE_TXRX_INFO +struct epctl { + uint8_t *addr; + uint16_t len; +}; +#endif + struct usb_dev { uint8_t configuration; uint8_t feature; uint8_t state; struct device_req dev_req; struct ctrl_data ctrl_data; +#ifdef USB_REQUIRE_TXRX_INFO + struct epctl epctl_tx[3]; + struct epctl epctl_rx[3]; +#endif }; enum { @@ -152,7 +175,22 @@ void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len); void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en); void usb_lld_stall_tx (int ep_num); void usb_lld_stall_rx (int ep_num); -#else +#elif defined(MCU_GD32VF1) +#define INTR_REQ_USB 86 +#define INTR_REQ_USB_WAKEUP 61 +void usb_lld_tx_enable_buf (struct usb_dev *dev, int ep_num, const void *buf, size_t len); +void usb_lld_rx_enable_buf (struct usb_dev *dev, int ep_num, void *buf, size_t len); + +/* EP_TYPE[1:0] EndPoint TYPE */ +#define EP_CONTROL 0x00 +#define EP_ISOCHRONOUS 0x01 +#define EP_BULK 0x02 +#define EP_INTERRUPT 0x03 + +void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int ep_type, int rx_en, int tx_en); +void usb_lld_stall_tx (struct usb_dev *dev, int ep_num); +void usb_lld_stall_rx (struct usb_dev *dev, int ep_num); +#else /* STM32L4 or STM32F103/GD32F103 */ #if defined(MCU_STM32L4) #define INTR_REQ_USB 67 #else