mbed-os/libraries/USBHost/USBHost/USBHALHost_RZ_A1.cpp

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