Adaptation of the official mbed USBHost repository to work with the LPC4088 Display Module

Dependents:   DMSupport DMSupport DMSupport DMSupport

Fork of DM_USBHost by EmbeddedArtists AB

Revision:
27:aa2fd412f1d3
Parent:
24:868cbfe611a7
Child:
31:9a462d032742
diff -r 607951c26872 -r aa2fd412f1d3 USBHost/USBHALHost.cpp
--- a/USBHost/USBHALHost.cpp	Mon Aug 18 13:45:26 2014 +0100
+++ b/USBHost/USBHALHost.cpp	Tue Dec 02 15:16:39 2014 +0000
@@ -39,7 +39,28 @@
 
 #define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
 
-static volatile uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM1"),aligned(256)));  //256 bytes aligned!
+// Put the USB structures in the only memory accessible by the USB stack - the AHBSRAM0
+static volatile uint8_t usb_buf[TOTAL_SIZE] __attribute__((section("AHBSRAM0"),aligned(256)));// __attribute__((section("AHBSRAM0"),aligned(256)));  //256 bytes aligned!
+
+// A very basic implementation of malloc to allocate in the AHBSRAM0
+#define SAFE_MEM_TO_USE     (10*1024)
+#define SAFE_MEM_BLOCK_SIZE (512)
+#define SAFE_MEM_NUM_BLOCKS (SAFE_MEM_TO_USE/SAFE_MEM_BLOCK_SIZE)
+typedef struct {
+    volatile uint8_t* ptr;
+    bool used;
+} safe_mem_info_t;
+static safe_mem_info_t safe_mem_list[SAFE_MEM_NUM_BLOCKS];
+static uint8_t safe_mem_data[SAFE_MEM_TO_USE] __attribute__((section("AHBSRAM0"),aligned(256)));//__attribute__((section("AHBSRAM0"),aligned));  //256 bytes aligned!
+
+// To detect when memory outside of the AHBSRAM0 is passed to the USB stack
+void assert_mem_region(uint32_t ptr)
+{
+    if (( ptr != 0) && ((ptr & 0xff000000) != 0x20000000)) {
+        USB_ERR("0x%08x not in USB MEM", ptr);
+        mbed_die();
+    }
+}
 
 USBHALHost * USBHALHost::instHost;
 
@@ -53,23 +74,64 @@
     for (int i = 0; i < MAX_TD; i++) {
         tdBufAlloc[i] = false;
     }
+    for (int i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
+        safe_mem_list[i].used = false;
+        safe_mem_list[i].ptr = safe_mem_data + SAFE_MEM_BLOCK_SIZE*i;
+    }
 }
 
+uint8_t* USBHALHost::getSafeMem(uint32_t size) {
+    uint8_t* result = NULL;
+    if (size > 512) {
+        USB_ERR("getSafeMem(%u) not supported", size);
+    } else {
+        safemem_mutex.lock();
+        for (int i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
+            if (!safe_mem_list[i].used) {
+                safe_mem_list[i].used = true;
+                result = (uint8_t*)safe_mem_list[i].ptr;
+                break;
+            }
+        }
+        safemem_mutex.unlock();
+    }
+    if (result == NULL) {
+        USB_ERR("getSafeMem(%u) failed to allocate", size);
+    }
+    return result;
+}
+
+void USBHALHost::returnSafeMem(uint8_t* mem) {
+    safemem_mutex.lock();
+    int i;
+    for (i = 0; i < SAFE_MEM_NUM_BLOCKS; i++) {
+        if (safe_mem_list[i].ptr == mem) {
+            safe_mem_list[i].used = false;
+            break;
+        }
+    }
+    safemem_mutex.unlock();
+    if (i == SAFE_MEM_NUM_BLOCKS) {
+      USB_ERR("returnSafeMem(%p) not allocated", mem);
+    }
+}
+
+
 void USBHALHost::init() {
     NVIC_DisableIRQ(USB_IRQn);
 
     //Cut power
     LPC_SC->PCONP &= ~(1UL<<31);
-    wait_ms(100);
+    wait_ms(1000);
 
     // turn on power for USB
     LPC_SC->PCONP       |= (1UL<<31);
 
     // Enable USB host clock, port selection and AHB clock
-    LPC_USB->USBClkCtrl |= CLOCK_MASK;
+    LPC_USB->USBClkCtrl = 0x19;
 
     // Wait for clocks to become available
-    while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
+    while ((LPC_USB->USBClkSt & 0x19) != 0x19);
 
     // it seems the bits[0:1] mean the following
     // 0: U1=device, U2=host
@@ -77,25 +139,39 @@
     // 2: reserved
     // 3: U1=host, U2=device
     // NB: this register is only available if OTG clock (aka "port select") is enabled!!
-    // since we don't care about port 2, set just bit 0 to 1 (U1=host)
-    LPC_USB->OTGStCtrl |= 1;
-
-    // now that we've configured the ports, we can turn off the portsel clock
-    LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+    LPC_USB->OTGStCtrl = 1;
 
     // configure USB D+/D- pins
-    // P0[29] = USB_D+, 01
-    // P0[30] = USB_D-, 01
-    LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
-    LPC_PINCON->PINSEL1 |=  ((1<<26) | (1<<28));
+
+    LPC_IOCON->P0_29 &= ~0x9F; // USB_D+1
+    LPC_IOCON->P0_29 |=  0x01; // USB_D+1
+    LPC_IOCON->P0_30 &= ~0x9F; // USB_D-1
+    LPC_IOCON->P0_30 |=  0x01; // USB_D-1
+
+    LPC_IOCON->P0_31 &= ~0x9F; // USB_D+2
+    LPC_IOCON->P0_31 |=  0x01; // USB_D+2
+
+    LPC_IOCON->P1_30 &= ~0x1F; // USB_PWRD2
+    LPC_IOCON->P1_30 |=  0x01; // USB_PWRD2
+    LPC_IOCON->P1_31 &= ~0x1F; // USB_OVRCR2
+    LPC_IOCON->P1_31 |=  0x01; // USB_OVRCR2
+
+    LPC_IOCON->P0_14 &= ~0x1F; // USB ID Pin as GPIO, output, high
+    LPC_GPIO0->DIR   |= (1<<14);
+    LPC_GPIO0->SET    = (1<<14);
+    LPC_IOCON->P4_31 &= ~0x1F; // USB CONN Pin as GPIO, output, low
+    LPC_GPIO4->DIR   |= (1UL<<31);
+    LPC_GPIO4->CLR    = (1UL<<31);
+
+    USB_DBG("initialize OHCI\n");
+
+    // Wait 100 ms before apply reset
+    wait_ms(100);
 
     LPC_USB->HcControl       = 0; // HARDWARE RESET
     LPC_USB->HcControlHeadED = 0; // Initialize Control list head to Zero
     LPC_USB->HcBulkHeadED    = 0; // Initialize Bulk list head to Zero
 
-    // Wait 100 ms before apply reset
-    wait_ms(100);
-
     // software reset
     LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
 
@@ -105,6 +181,7 @@
 
     // Put HC in operational state
     LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+
     // Set Global Power
     LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;
 
@@ -119,15 +196,19 @@
     NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr));
     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+    LPC_USB->HcRhPortStatus2 = OR_RH_PORT_CSC;
+    LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
+    
+    resetRootHub();
 
+    NVIC_SetPriority(USB_IRQn, 0);
     NVIC_EnableIRQ(USB_IRQn);
 
-    // Check for any connected devices
-    if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) {
+    if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CCS) {
         //Device connected
         wait_ms(150);
-        USB_DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1);
-        deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA);
+        USB_DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus2);
+        deviceConnected(0, 2, LPC_USB->HcRhPortStatus2 & OR_RH_PORT_LSDA);
     }
 }
 
@@ -216,8 +297,8 @@
             return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
         }
     }
-    perror("Could not allocate ED\r\n");
-    return NULL; //Could not alloc ED
+    USB_ERR("Could not allocate ED\r\n");
+    return NULL;
 }
 
 volatile uint8_t * USBHALHost::getTD() {
@@ -228,8 +309,8 @@
             return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
         }
     }
-    perror("Could not allocate TD\r\n");
-    return NULL; //Could not alloc TD
+    USB_ERR("Could not allocate TD\r\n");
+    return NULL;
 }
 
 
@@ -247,6 +328,11 @@
 
 
 void USBHALHost::resetRootHub() {
+
+    DigitalOut usb2_vbus_en(P0_12);
+    usb2_vbus_en = 1;
+    wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
+
     // Initiate port reset
     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS;
 
@@ -254,6 +340,19 @@
 
     // ...and clear port reset signal
     LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+
+    usb2_vbus_en = 0;
+
+    // Initiate port reset
+    LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRS;
+
+    while (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_PRS);
+    
+    // ...and clear port reset signal
+    LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
+    
+    usb2_vbus_en = 1;
+    wait_ms(200); /* Wait for 100 MS after port reset  */
 }
 
 
@@ -266,25 +365,24 @@
 void USBHALHost::UsbIrqhandler() {
     if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process?
     {
-
         uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable;
-
+        
         // Root hub status change interrupt
         if (int_status & OR_INTR_STATUS_RHSC) {
-            if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) {
+            if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CSC) {
                 if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) {
                     // When DRWE is on, Connect Status Change
                     // means a remote wakeup event.
                 } else {
 
                     //Root device connected
-                    if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) {
+                    if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_CCS) {
 
                         // wait 150ms to avoid bounce
                         wait_ms(150);
 
-                        //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
-                        deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA);
+                        //Hub 0 (root hub), Port 2 (count starts at 1), Low or High speed
+                        deviceConnected(0, 2, LPC_USB->HcRhPortStatus2 & OR_RH_PORT_LSDA);
                     }
 
                     //Root device disconnected
@@ -297,7 +395,7 @@
                         // wait 200ms to avoid bounce
                         wait_ms(200);
 
-                        deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
+                        deviceDisconnected(0, 2, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
 
                         if (int_status & OR_INTR_STATUS_WDH) {
                             usb_hcca->DoneHead = 0;
@@ -305,10 +403,10 @@
                         }
                     }
                 }
-                LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+                LPC_USB->HcRhPortStatus2 = OR_RH_PORT_CSC;
             }
-            if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) {
-                LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+            if (LPC_USB->HcRhPortStatus2 & OR_RH_PORT_PRSC) {
+                LPC_USB->HcRhPortStatus2 = OR_RH_PORT_PRSC;
             }
             LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC;
         }