Browse Source

HS USB: Initial commit.

Keir Fraser 5 years ago
parent
commit
98b49d27be
9 changed files with 199 additions and 37 deletions
  1. 3 1
      inc/usb.h
  2. 11 11
      src/floppy.c
  3. 2 2
      src/usb/cdc_acm.c
  4. 85 1
      src/usb/config.c
  5. 16 4
      src/usb/core.c
  6. 7 1
      src/usb/defs.h
  7. 61 16
      src/usb/hw_dwc_otg.c
  8. 3 0
      src/usb/hw_dwc_otg.h
  9. 11 1
      src/usb/hw_f1.c

+ 3 - 1
inc/usb.h

@@ -9,8 +9,10 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-/* Full Speed Max Packet Size */
+/* Max Packet Size */
 #define USB_FS_MPS 64
+#define USB_HS_MPS 512
+extern unsigned int usb_bulk_mps;
 
 /* Class-specific callback hooks */
 struct usb_class_ops {

+ 11 - 11
src/floppy.c

@@ -264,7 +264,7 @@ static void floppy_end_command(void *ack, unsigned int ack_len)
     u_cons = u_prod = 0;
     if (floppy_state == ST_command_wait)
         act_led(FALSE);
-    if (ack_len == USB_FS_MPS) {
+    if (ack_len == usb_bulk_mps) {
         ASSERT(floppy_state == ST_command_wait);
         floppy_state = ST_zlp;
     }
@@ -289,7 +289,7 @@ static struct {
     int ticks_since_index;
     uint32_t ticks_since_flux;
     uint32_t index_ticks[15];
-    uint8_t packet[USB_FS_MPS];
+    uint8_t packet[USB_HS_MPS];
 } rw;
 
 static void rdata_encode_flux(void)
@@ -449,12 +449,12 @@ static void floppy_read(void)
 
         }
 
-    } else if ((avail < USB_FS_MPS)
+    } else if ((avail < usb_bulk_mps)
                && !rw.packet_ready
                && ep_tx_ready(EP_TX)) {
 
         /* Final packet, including ACK byte (NUL). */
-        memset(rw.packet, 0, USB_FS_MPS);
+        memset(rw.packet, 0, usb_bulk_mps);
         make_read_packet(avail);
         floppy_state = ST_command_wait;
         floppy_end_command(rw.packet, avail+1);
@@ -462,11 +462,11 @@ static void floppy_read(void)
 
     }
 
-    if (!rw.packet_ready && (avail >= USB_FS_MPS))
-        make_read_packet(USB_FS_MPS);
+    if (!rw.packet_ready && (avail >= usb_bulk_mps))
+        make_read_packet(usb_bulk_mps);
 
     if (rw.packet_ready && ep_tx_ready(EP_TX)) {
-        usb_write(EP_TX, rw.packet, USB_FS_MPS);
+        usb_write(EP_TX, rw.packet, usb_bulk_mps);
         rw.packet_ready = FALSE;
     }
 }
@@ -819,15 +819,15 @@ static void source_bytes(void)
     if (!ep_tx_ready(EP_TX))
         return;
 
-    if (ss.todo < USB_FS_MPS) {
+    if (ss.todo < usb_bulk_mps) {
         floppy_state = ST_command_wait;
         floppy_end_command(rw.packet, ss.todo);
         return; /* FINISHED */
     }
 
-    usb_write(EP_TX, rw.packet, USB_FS_MPS);
-    ss.todo -= USB_FS_MPS;
-    ss_update_deltas(USB_FS_MPS);
+    usb_write(EP_TX, rw.packet, usb_bulk_mps);
+    ss.todo -= usb_bulk_mps;
+    ss_update_deltas(usb_bulk_mps);
 }
 
 static void sink_bytes(void)

+ 2 - 2
src/usb/cdc_acm.c

@@ -111,9 +111,9 @@ bool_t cdc_acm_set_configuration(void)
     /* Notification Element (D->H) */
     usb_configure_ep(0x81, EPT_INTERRUPT, 0);
     /* Bulk Pipe (H->D) */
-    usb_configure_ep(0x02, bulk_type, USB_FS_MPS);
+    usb_configure_ep(0x02, bulk_type, usb_bulk_mps);
     /* Bulk Pipe (D->H) */
-    usb_configure_ep(0x83, bulk_type, USB_FS_MPS);
+    usb_configure_ep(0x83, bulk_type, usb_bulk_mps);
 
     usb_cdc_acm_ops.configure();
 

+ 85 - 1
src/usb/config.c

@@ -22,7 +22,16 @@ const uint8_t device_descriptor[] aligned(2) = {
     1          /* Number of configurations */
 };
 
-const uint8_t config_descriptor[] aligned(2) = {
+const uint8_t device_qualifier[] aligned(2) = {
+    10,        /* Length */
+    DESC_DEVICE_QUALIFIER,
+    0x00,0x02, /* USB 2.0 */
+    2, 0, 0,   /* Class, Subclass, Protocol: CDC */
+    64,        /* Max Packet Size */
+    1          /* Number of configurations */
+};
+
+const uint8_t config_fs_descriptor[] aligned(2) = {
     0x09, /* 0 bLength */
     DESC_CONFIGURATION, /* 1 bDescriptortype - Configuration*/
     0x43, 0x00, /* 2 wTotalLength */
@@ -97,6 +106,81 @@ const uint8_t config_descriptor[] aligned(2) = {
     0x00 /* 6 bInterval */
 };
 
+const uint8_t config_hs_descriptor[] aligned(2) = {
+    0x09, /* 0 bLength */
+    DESC_CONFIGURATION, /* 1 bDescriptortype - Configuration*/
+    0x43, 0x00, /* 2 wTotalLength */
+    0x02, /* 4 bNumInterfaces */
+    0x01, /* 5 bConfigurationValue */
+    0x00, /* 6 iConfiguration - index of string */
+    0x80, /* 7 bmAttributes - Bus powered */
+    0xC8, /* 8 bMaxPower - 400mA */
+/* CDC Communication interface */
+    0x09, /* 0 bLength */
+    DESC_INTERFACE, /* 1 bDescriptorType - Interface */
+    0x00, /* 2 bInterfaceNumber - Interface 0 */
+    0x00, /* 3 bAlternateSetting */
+    0x01, /* 4 bNumEndpoints */
+    2, 2, 1, /* CDC ACM, AT Command Protocol */
+    0x00, /* 8 iInterface - No string descriptor */
+/* Header Functional descriptor */
+    0x05, /* 0 bLength */
+    DESC_CS_INTERFACE, /* 1 bDescriptortype, CS_INTERFACE */
+    0x00, /* 2 bDescriptorsubtype, HEADER */
+    0x10, 0x01, /* 3 bcdCDC */
+/* ACM Functional descriptor */
+    0x04, /* 0 bLength */
+    DESC_CS_INTERFACE, /* 1 bDescriptortype, CS_INTERFACE */
+    0x02, /* 2 bDescriptorsubtype, ABSTRACT CONTROL MANAGEMENT */
+    0x02, /* 3 bmCapabilities: Supports subset of ACM commands */
+/* Union Functional descriptor */
+    0x05, /* 0 bLength */
+    DESC_CS_INTERFACE,/* 1 bDescriptortype, CS_INTERFACE */
+    0x06, /* 2 bDescriptorsubtype, UNION */
+    0x00, /* 3 bControlInterface - Interface 0 */
+    0x01, /* 4 bSubordinateInterface0 - Interface 1 */
+/* Call Management Functional descriptor */
+    0x05, /* 0 bLength */
+    DESC_CS_INTERFACE,/* 1 bDescriptortype, CS_INTERFACE */
+    0x01, /* 2 bDescriptorsubtype, CALL MANAGEMENT */
+    0x03, /* 3 bmCapabilities, DIY */
+    0x01, /* 4 bDataInterface */
+/* Notification Endpoint descriptor */
+    0x07, /* 0 bLength */
+    DESC_ENDPOINT, /* 1 bDescriptorType */
+    0x81, /* 2 bEndpointAddress */
+    0x03, /* 3 bmAttributes */
+    0x40, /* 4 wMaxPacketSize - Low */
+    0x00, /* 5 wMaxPacketSize - High */
+    0x10, /* 6 bInterval */
+/* CDC Data interface */
+    0x09, /* 0 bLength */
+    DESC_INTERFACE, /* 1 bDescriptorType */
+    0x01, /* 2 bInterfaceNumber */
+    0x00, /* 3 bAlternateSetting */
+    0x02, /* 4 bNumEndpoints */
+    USB_CLASS_CDC_DATA, /* 5 bInterfaceClass */
+    0x00, /* 6 bInterfaceSubClass */
+    0x00, /* 7 bInterfaceProtocol*/
+    0x00, /* 8 iInterface - No string descriptor*/
+/* Data OUT Endpoint descriptor */
+    0x07, /* 0 bLength */
+    DESC_ENDPOINT, /* 1 bDescriptorType */
+    0x02, /* 2 bEndpointAddress */
+    0x02, /* 3 bmAttributes */
+    0x00, /* 4 wMaxPacketSize - Low */
+    0x02, /* 5 wMaxPacketSize - High */
+    0x00, /* 6 bInterval */
+/* Data IN Endpoint descriptor */
+    0x07, /* 0 bLength */
+    DESC_ENDPOINT, /* 1 bDescriptorType */
+    0x83, /* 2 bEndpointAddress */
+    0x02, /* 3 bmAttributes */
+    0x00, /* 4 wMaxPacketSize - Low byte */
+    0x02, /* 5 wMaxPacketSize - High byte */
+    0x00 /* 6 bInterval */
+};
+
 char serial_string[32];
 char * const string_descriptors[] = {
     "\x09\x04", /* LANGID: US English */

+ 16 - 4
src/usb/core.c

@@ -9,6 +9,8 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
+unsigned int usb_bulk_mps = USB_FS_MPS;
+
 struct ep0 ep0;
 
 void usb_init(void)
@@ -41,9 +43,19 @@ static bool_t handle_control_request(void)
         if ((type == DESC_DEVICE) && (idx == 0)) {
             ep0.data_len = device_descriptor[0]; /* bLength */
             memcpy(ep0.data, device_descriptor, ep0.data_len);
+        } else if ((type == DESC_DEVICE_QUALIFIER) && (idx == 0)) {
+            if ((handled = hw_has_highspeed())) {
+                ep0.data_len = device_qualifier[0]; /* bLength */
+                memcpy(ep0.data, device_qualifier, ep0.data_len);
+            }
         } else if ((type == DESC_CONFIGURATION) && (idx == 0)) {
-            ep0.data_len = config_descriptor[2]; /* wTotalLength */
-            memcpy(ep0.data, config_descriptor, ep0.data_len);
+            if (hw_is_highspeed()) {
+                ep0.data_len = config_hs_descriptor[2]; /* wTotalLength */
+                memcpy(ep0.data, config_hs_descriptor, ep0.data_len);
+            } else {
+                ep0.data_len = config_fs_descriptor[2]; /* wTotalLength */
+                memcpy(ep0.data, config_fs_descriptor, ep0.data_len);
+            }
         } else if ((type == DESC_STRING) && (idx < NR_STRING_DESC)) {
             const char *s = string_descriptors[idx];
             uint16_t *odat = (uint16_t *)ep0.data;
@@ -107,7 +119,7 @@ static void usb_write_ep0(void)
     if ((ep0.tx.todo < 0) || !ep_tx_ready(0))
         return;
 
-    len = min_t(uint32_t, ep0.tx.todo, USB_FS_MPS);
+    len = min_t(uint32_t, ep0.tx.todo, EP0_MPS);
     usb_write(0, ep0.tx.p, len);
 
     ep0.tx.p += len;
@@ -117,7 +129,7 @@ static void usb_write_ep0(void)
         /* USB Spec 1.1, Section 5.5.3: Data stage of a control transfer is
          * complete when we have transferred the exact amount of data specified
          * during Setup *or* transferred a short/zero packet. */
-        if (!ep0.tx.trunc || (len < USB_FS_MPS))
+        if (!ep0.tx.trunc || (len < EP0_MPS))
             ep0.tx.todo = -1;
     }
 }

+ 7 - 1
src/usb/defs.h

@@ -43,8 +43,12 @@ struct packed usb_device_request {
     uint16_t wLength;
 };
 
+#define EP0_MPS 64
+
 extern const uint8_t device_descriptor[];
-extern const uint8_t config_descriptor[];
+extern const uint8_t device_qualifier[];
+extern const uint8_t config_fs_descriptor[];
+extern const uint8_t config_hs_descriptor[];
 
 #define NR_STRING_DESC 4
 extern char * const string_descriptors[];
@@ -78,6 +82,8 @@ void usb_stall(uint8_t ep);
 void usb_setaddr(uint8_t addr);
 void hw_usb_init(void);
 void hw_usb_deinit(void);
+bool_t hw_has_highspeed(void);
+bool_t hw_is_highspeed(void);
 
 #define WARN printk
 

+ 61 - 16
src/usb/hw_dwc_otg.c

@@ -11,8 +11,10 @@
 
 #include "hw_dwc_otg.h"
 
-struct rx_buf {
-    uint32_t data[USB_FS_MPS / 4];
+static bool_t is_hs;
+
+static struct rx_buf {
+    uint32_t data[USB_HS_MPS / 4];
     uint32_t count;
 } rx_buf0[1], rx_bufn[32];
 
@@ -24,6 +26,16 @@ static struct ep {
     bool_t rx_active, tx_ready;
 } eps[conf_nr_ep];
 
+bool_t hw_has_highspeed(void)
+{
+    return conf_iface == IFACE_HS_EMBEDDED;
+}
+
+bool_t hw_is_highspeed(void)
+{
+    return is_hs;
+}
+
 static void core_reset(void)
 {
     do { delay_us(1); } while (!(otg->grstctl & OTG_GRSTCTL_AHBIDL));
@@ -31,6 +43,27 @@ static void core_reset(void)
     do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_CSRST);
 }
 
+static void hsphyc_init(void)
+{
+    /* Enable the LDO and wait for it to be ready. */
+    hsphyc->ldo |= HSPHYC_LDO_ENABLE;
+    do { delay_us(1); } while (!(hsphyc->ldo & HSPHYC_LDO_STATUS));
+
+    /* HSE must be 25MHz! SEL(3) for 16MHz. */
+    hsphyc->pll1 |= HSPHYC_PLL1_SEL(5);
+
+    /* Magic values from the LL driver. We can probably discard them. */
+    hsphyc->tune |= (HSPHYC_TUNE_HSDRVCHKITRIM(7) |
+                     HSPHYC_TUNE_HSDRVRFRED |
+                     HSPHYC_TUNE_HSDRVDCCUR |
+                     HSPHYC_TUNE_INCURRINT |
+                     HSPHYC_TUNE_INCURREN);
+
+    /* Enable the PLL and wait to stabilise. */
+    hsphyc->pll1 |= HSPHYC_PLL1_EN;
+    delay_ms(2);
+}
+
 static void flush_tx_fifo(int num)
 {
     otg->grstctl = OTG_GRSTCTL_TXFFLSH | OTG_GRSTCTL_TXFNUM(num);
@@ -60,7 +93,7 @@ static void prepare_rx(uint8_t epnr)
 {
     struct ep *ep = &eps[epnr];
     OTG_DOEP doep = &otg_doep[epnr];
-    uint16_t mps = (epnr == 0) ? 64 : (doep->ctl & 0x7ff);
+    uint16_t mps = (epnr == 0) ? EP0_MPS : (doep->ctl & 0x7ff);
     uint32_t tsiz = doep->tsiz & 0xe0000000;
 
     tsiz |= OTG_DOEPTSZ_PKTCNT(ep->rx_nr);
@@ -117,6 +150,7 @@ void hw_usb_init(void)
 
     switch (conf_port) {
     case PORT_FS:
+        ASSERT(conf_iface == IFACE_FS);
         gpio_set_af(gpioa, 11, 10);
         gpio_set_af(gpioa, 12, 10);
         gpio_configure_pin(gpioa, 11, AFO_pushpull(IOSPD_HIGH));
@@ -124,11 +158,18 @@ void hw_usb_init(void)
         rcc->ahb2enr |= RCC_AHB2ENR_OTGFSEN;
         break;
     case PORT_HS:
-        gpio_set_af(gpiob, 14, 12);
-        gpio_set_af(gpiob, 15, 12);
-        gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
-        gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
-        rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+        ASSERT((conf_iface == IFACE_FS) || (conf_iface == IFACE_HS_EMBEDDED));
+        if (conf_iface == IFACE_FS) {
+            gpio_set_af(gpiob, 14, 12);
+            gpio_set_af(gpiob, 15, 12);
+            gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
+            gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+        } else {
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSULPIEN;
+            rcc->apb2enr |= RCC_APB2ENR_OTGPHYCEN;
+        }
         break;
     default:
         ASSERT(0);
@@ -148,7 +189,11 @@ void hw_usb_init(void)
         /* Activate FS transceiver. */
         otg->gccfg |= OTG_GCCFG_PWRDWN;
     } else {
-        ASSERT(0);
+        /* Disable the FS transceiver, enable the HS transceiver. */
+        otg->gccfg &= ~OTG_GCCFG_PWRDWN;
+        otg->gccfg |= OTG_GCCFG_PHYHSEN;
+        hsphyc_init();
+        core_reset();
     }
 
     /*
@@ -175,9 +220,9 @@ void hw_usb_init(void)
 
     /* USB_SetDevSpeed */
     if (conf_iface == IFACE_FS) {
-        otgd->dcfg = OTG_DCFG_DSPD(3); /* Full Speed */
+        otgd->dcfg = OTG_DCFG_DSPD(DSPD_FULL);
     } else {
-        ASSERT(0);
+        otgd->dcfg = OTG_DCFG_DSPD(DSPD_HIGH);
     }
 
     flush_tx_fifo(0x10);
@@ -468,17 +513,17 @@ void usb_process(void)
     }
 
     if (gintsts & OTG_GINT_ENUMDNE) {
-        bool_t hs;
-        printk("[ENUMDNE]\n");
         /* USB_ActivateSetup */
         otg_diep[0].ctl &= ~OTG_DIEPCTL_MPSIZ(0x7ff);
         otgd->dctl |= OTG_DCTL_CGINAK;
         /* USB_SetTurnaroundTime */
-        hs = is_high_speed();
+        is_hs = is_high_speed();
+        usb_bulk_mps = is_hs ? USB_HS_MPS : USB_FS_MPS;
         /* Ref. Table 232, FS Mode */
-        otg->gusbcfg |= OTG_GUSBCFG_TRDT(hs ? 9 : 6);
-        usb_configure_ep(0, EPT_CONTROL, USB_FS_MPS);
+        otg->gusbcfg |= OTG_GUSBCFG_TRDT(is_hs ? 9 : 6);
+        usb_configure_ep(0, EPT_CONTROL, EP0_MPS);
         otg->gintsts = OTG_GINT_ENUMDNE;
+        printk("[ENUMDNE: %cS]\n", is_hs ? 'H' : 'F');
     }
 
     if (gintsts & OTG_GINT_USBRST) {

+ 3 - 0
src/usb/hw_dwc_otg.h

@@ -271,6 +271,9 @@ struct otg_dfifo { /* 1000.. */
 #define OTG_DCFG_DAD(x)       ((x)<<4)
 #define OTG_DCFG_NZLSOHSK     (1u<< 2)
 #define OTG_DCFG_DSPD(x)      ((x)<<0)
+#define DSPD_HIGH             0u
+#define DSPD_HIGH_IN_FULL     1u
+#define DSPD_FULL             3u
 
 #define OTG_DCTL_DSBESLRJCT   (1u<<18)
 #define OTG_DCTL_POPRGDNE     (1u<<11)

+ 11 - 1
src/usb/hw_f1.c

@@ -22,6 +22,16 @@ static struct {
     bool_t tx_ready;
 } eps[8];
 
+bool_t hw_has_highspeed(void)
+{
+    return FALSE;
+}
+
+bool_t hw_is_highspeed(void)
+{
+    return FALSE;
+}
+
 void hw_usb_init(void)
 {
     /* Turn on clock. */
@@ -281,7 +291,7 @@ static void handle_reset(void)
     /* Prepare for Enumeration: Set up Endpoint 0 at Address 0. */
     pending_addr = 0;
     buf_end = 64;
-    usb_configure_ep(0, EPT_CONTROL, USB_FS_MPS);
+    usb_configure_ep(0, EPT_CONTROL, EP0_MPS);
     usb->daddr = USB_DADDR_EF | USB_DADDR_ADD(0);
     usb->istr &= ~USB_ISTR_RESET;
 }