mirror of https://github.com/ARMmbed/mbed-os.git
293 lines
9.1 KiB
C++
293 lines
9.1 KiB
C++
/* mbed USBHost Library
|
|
* Copyright (c) 2006-2013 ARM Limited
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if defined(TARGET_RZ_A1H)
|
|
|
|
#include "mbed.h"
|
|
#include "USBHALHost.h"
|
|
#include "dbg.h"
|
|
|
|
#include "ohci_wrapp_RZ_A1.h"
|
|
|
|
|
|
#define HCCA_SIZE sizeof(HCCA)
|
|
#define ED_SIZE sizeof(HCED)
|
|
#define TD_SIZE sizeof(HCTD)
|
|
|
|
#define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
|
|
#define ALIGNE_MSK (0x0000000F)
|
|
|
|
static volatile uint8_t usb_buf[TOTAL_SIZE + ALIGNE_MSK]; //16 bytes aligned!
|
|
|
|
USBHALHost * USBHALHost::instHost;
|
|
|
|
USBHALHost::USBHALHost() {
|
|
instHost = this;
|
|
memInit();
|
|
memset((void*)usb_hcca, 0, HCCA_SIZE);
|
|
for (int i = 0; i < MAX_ENDPOINT; i++) {
|
|
edBufAlloc[i] = false;
|
|
}
|
|
for (int i = 0; i < MAX_TD; i++) {
|
|
tdBufAlloc[i] = false;
|
|
}
|
|
}
|
|
|
|
void USBHALHost::init() {
|
|
ohciwrapp_init(&_usbisr);
|
|
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, 1); // HARDWARE RESET
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROLHEADED, 0); // Initialize Control list head to Zero
|
|
ohciwrapp_reg_w(OHCI_REG_BULKHEADED, 0); // Initialize Bulk list head to Zero
|
|
|
|
// Wait 100 ms before apply reset
|
|
wait_ms(100);
|
|
|
|
// software reset
|
|
ohciwrapp_reg_w(OHCI_REG_COMMANDSTATUS, OR_CMD_STATUS_HCR);
|
|
|
|
// Write Fm Interval and Largest Data Packet Counter
|
|
ohciwrapp_reg_w(OHCI_REG_FMINTERVAL, DEFAULT_FMINTERVAL);
|
|
ohciwrapp_reg_w(OHCI_REG_PERIODICSTART, FI * 90 / 100);
|
|
|
|
// Put HC in operational state
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, (ohciwrapp_reg_r(OHCI_REG_CONTROL) & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER);
|
|
// Set Global Power
|
|
ohciwrapp_reg_w(OHCI_REG_RHSTATUS, OR_RH_STATUS_LPSC);
|
|
|
|
ohciwrapp_reg_w(OHCI_REG_HCCA, (uint32_t)(usb_hcca));
|
|
|
|
// Clear Interrrupt Status
|
|
ohciwrapp_reg_w(OHCI_REG_INTERRUPTSTATUS, ohciwrapp_reg_r(OHCI_REG_INTERRUPTSTATUS));
|
|
|
|
ohciwrapp_reg_w(OHCI_REG_INTERRUPTENABLE, OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC);
|
|
|
|
// Enable the USB Interrupt
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_CSC);
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_PRSC);
|
|
|
|
// Check for any connected devices
|
|
if (ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_CCS) {
|
|
//Device connected
|
|
wait_ms(150);
|
|
USB_DBG("Device connected (%08x)\n\r", ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1));
|
|
deviceConnected(0, 1, ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_LSDA);
|
|
}
|
|
}
|
|
|
|
uint32_t USBHALHost::controlHeadED() {
|
|
return ohciwrapp_reg_r(OHCI_REG_CONTROLHEADED);
|
|
}
|
|
|
|
uint32_t USBHALHost::bulkHeadED() {
|
|
return ohciwrapp_reg_r(OHCI_REG_BULKHEADED);
|
|
}
|
|
|
|
uint32_t USBHALHost::interruptHeadED() {
|
|
return usb_hcca->IntTable[0];
|
|
}
|
|
|
|
void USBHALHost::updateBulkHeadED(uint32_t addr) {
|
|
ohciwrapp_reg_w(OHCI_REG_BULKHEADED, addr);
|
|
}
|
|
|
|
|
|
void USBHALHost::updateControlHeadED(uint32_t addr) {
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROLHEADED, addr);
|
|
}
|
|
|
|
void USBHALHost::updateInterruptHeadED(uint32_t addr) {
|
|
usb_hcca->IntTable[0] = addr;
|
|
}
|
|
|
|
|
|
void USBHALHost::enableList(ENDPOINT_TYPE type) {
|
|
uint32_t wk_data;
|
|
|
|
switch(type) {
|
|
case CONTROL_ENDPOINT:
|
|
ohciwrapp_reg_w(OHCI_REG_COMMANDSTATUS, OR_CMD_STATUS_CLF);
|
|
wk_data = (ohciwrapp_reg_r(OHCI_REG_CONTROL) | OR_CONTROL_CLE);
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
break;
|
|
case ISOCHRONOUS_ENDPOINT:
|
|
break;
|
|
case BULK_ENDPOINT:
|
|
ohciwrapp_reg_w(OHCI_REG_COMMANDSTATUS, OR_CMD_STATUS_BLF);
|
|
wk_data = (ohciwrapp_reg_r(OHCI_REG_CONTROL) | OR_CONTROL_BLE);
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
break;
|
|
case INTERRUPT_ENDPOINT:
|
|
wk_data = (ohciwrapp_reg_r(OHCI_REG_CONTROL) | OR_CONTROL_PLE);
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool USBHALHost::disableList(ENDPOINT_TYPE type) {
|
|
uint32_t wk_data;
|
|
|
|
switch(type) {
|
|
case CONTROL_ENDPOINT:
|
|
wk_data = ohciwrapp_reg_r(OHCI_REG_CONTROL);
|
|
if(wk_data & OR_CONTROL_CLE) {
|
|
wk_data &= ~OR_CONTROL_CLE;
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
return true;
|
|
}
|
|
return false;
|
|
case ISOCHRONOUS_ENDPOINT:
|
|
return false;
|
|
case BULK_ENDPOINT:
|
|
wk_data = ohciwrapp_reg_r(OHCI_REG_CONTROL);
|
|
if(wk_data & OR_CONTROL_BLE) {
|
|
wk_data &= ~OR_CONTROL_BLE;
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
return true;
|
|
}
|
|
return false;
|
|
case INTERRUPT_ENDPOINT:
|
|
wk_data = ohciwrapp_reg_r(OHCI_REG_CONTROL);
|
|
if(wk_data & OR_CONTROL_PLE) {
|
|
wk_data &= ~OR_CONTROL_PLE;
|
|
ohciwrapp_reg_w(OHCI_REG_CONTROL, wk_data);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void USBHALHost::memInit() {
|
|
volatile uint8_t *p_wk_buf = (uint8_t *)(((uint32_t)usb_buf + ALIGNE_MSK) & ~ALIGNE_MSK);
|
|
|
|
usb_hcca = (volatile HCCA *)p_wk_buf;
|
|
usb_edBuf = (volatile uint8_t *)(p_wk_buf + HCCA_SIZE);
|
|
usb_tdBuf = (volatile uint8_t *)(p_wk_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE));
|
|
}
|
|
|
|
volatile uint8_t * USBHALHost::getED() {
|
|
for (int i = 0; i < MAX_ENDPOINT; i++) {
|
|
if ( !edBufAlloc[i] ) {
|
|
edBufAlloc[i] = true;
|
|
return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
|
|
}
|
|
}
|
|
perror("Could not allocate ED\r\n");
|
|
return NULL; //Could not alloc ED
|
|
}
|
|
|
|
volatile uint8_t * USBHALHost::getTD() {
|
|
int i;
|
|
for (i = 0; i < MAX_TD; i++) {
|
|
if ( !tdBufAlloc[i] ) {
|
|
tdBufAlloc[i] = true;
|
|
return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
|
|
}
|
|
}
|
|
perror("Could not allocate TD\r\n");
|
|
return NULL; //Could not alloc TD
|
|
}
|
|
|
|
|
|
void USBHALHost::freeED(volatile uint8_t * ed) {
|
|
int i;
|
|
i = (ed - usb_edBuf) / ED_SIZE;
|
|
edBufAlloc[i] = false;
|
|
}
|
|
|
|
void USBHALHost::freeTD(volatile uint8_t * td) {
|
|
int i;
|
|
i = (td - usb_tdBuf) / TD_SIZE;
|
|
tdBufAlloc[i] = false;
|
|
}
|
|
|
|
|
|
void USBHALHost::resetRootHub() {
|
|
// Initiate port reset
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_PRS);
|
|
|
|
while (ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_PRS);
|
|
|
|
// ...and clear port reset signal
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_PRSC);
|
|
}
|
|
|
|
|
|
void USBHALHost::_usbisr(void) {
|
|
if (instHost) {
|
|
instHost->UsbIrqhandler();
|
|
}
|
|
}
|
|
|
|
void USBHALHost::UsbIrqhandler() {
|
|
uint32_t int_status = ohciwrapp_reg_r(OHCI_REG_INTERRUPTSTATUS) & ohciwrapp_reg_r(OHCI_REG_INTERRUPTENABLE);
|
|
uint32_t data;
|
|
|
|
if (int_status != 0) { //Is there something to actually process?
|
|
// Root hub status change interrupt
|
|
if (int_status & OR_INTR_STATUS_RHSC) {
|
|
if (ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_CSC) {
|
|
if (ohciwrapp_reg_r(OHCI_REG_RHSTATUS) & OR_RH_STATUS_DRWE) {
|
|
// When DRWE is on, Connect Status Change
|
|
// means a remote wakeup event.
|
|
} else {
|
|
|
|
//Root device connected
|
|
if (ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & 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
|
|
data = ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_LSDA;
|
|
deviceConnected(0, 1, data);
|
|
}
|
|
|
|
//Root device disconnected
|
|
else {
|
|
|
|
if (!(int_status & OR_INTR_STATUS_WDH)) {
|
|
usb_hcca->DoneHead = 0;
|
|
}
|
|
|
|
deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
|
|
|
|
if (int_status & OR_INTR_STATUS_WDH) {
|
|
usb_hcca->DoneHead = 0;
|
|
ohciwrapp_reg_w(OHCI_REG_INTERRUPTSTATUS, OR_INTR_STATUS_WDH);
|
|
}
|
|
}
|
|
}
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_CSC);
|
|
}
|
|
if (ohciwrapp_reg_r(OHCI_REG_RHPORTSTATUS1) & OR_RH_PORT_PRSC) {
|
|
ohciwrapp_reg_w(OHCI_REG_RHPORTSTATUS1, OR_RH_PORT_PRSC);
|
|
}
|
|
ohciwrapp_reg_w(OHCI_REG_INTERRUPTSTATUS, OR_INTR_STATUS_RHSC);
|
|
}
|
|
|
|
// Writeback Done Head interrupt
|
|
if (int_status & OR_INTR_STATUS_WDH) {
|
|
transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
|
|
ohciwrapp_reg_w(OHCI_REG_INTERRUPTSTATUS, OR_INTR_STATUS_WDH);
|
|
}
|
|
}
|
|
}
|
|
#endif
|