Files
CherryUSB/port/ohci/usb_hc_ohci.c

240 lines
7.2 KiB
C

/*
* Copyright (c) 2024, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usb_hc_ohci.h"
#include "usb_hc_ehci.h"
int ohci_init(struct usbh_bus *bus)
{
volatile uint32_t timeout = 0;
uint32_t regval;
USB_LOG_INFO("OHCI hcrevision:0x%02x\r\n", (unsigned int)OHCI_HCOR->hcrevision);
OHCI_HCOR->hccontrol = 0;
OHCI_HCOR->hccontrolheaded = 0;
OHCI_HCOR->hcbulkheaded = 0;
OHCI_HCOR->hccmdsts = OHCI_CMDST_HCR;
while (OHCI_HCOR->hccmdsts & OHCI_CMDST_HCR) {
usb_osal_msleep(1);
timeout++;
if (timeout > 100) {
return -USB_ERR_TIMEOUT;
}
}
/* Frame Interval / Periodic Start.
*
* At 12Mbps, there are 12000 bit time in each 1Msec frame.
*/
#define BITS_PER_FRAME 12000
#define FI (BITS_PER_FRAME - 1)
#define FSMPS ((6 * (FI - 210)) / 7)
#define DEFAULT_FMINTERVAL ((FSMPS << OHCI_FMINT_FSMPS_SHIFT) | FI)
#define DEFAULT_PERSTART (((9 * BITS_PER_FRAME) / 10) - 1)
OHCI_HCOR->hcfminterval = DEFAULT_FMINTERVAL;
OHCI_HCOR->hcperiodicstart = DEFAULT_PERSTART;
/* Put HC in operational state */
regval = OHCI_HCOR->hccontrol;
regval &= ~OHCI_CTRL_HCFS_MASK;
regval |= OHCI_CTRL_HCFS_OPER;
OHCI_HCOR->hccontrol = regval;
/* Set global power in HcRhStatus */
OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP;
/* Set HCCA base address */
OHCI_HCOR->hchcca = 0;
/* Clear pending interrupts */
regval = OHCI_HCOR->hcintsts;
OHCI_HCOR->hcintsts = regval;
for (uint8_t port = 0; port < g_ehci_hcd[bus->hcd.hcd_id].n_pcc; port++) {
regval = OHCI_HCOR->hcrhportsts[port];
regval |= OHCI_RHPORTST_PPS;
OHCI_HCOR->hcrhportsts[port] = regval;
}
/* Enable OHCI interrupts */
OHCI_HCOR->hcinten = OHCI_INT_SO | OHCI_INT_RD | OHCI_INT_UE | OHCI_INT_OC |
OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE;
return 0;
}
int ohci_deinit(struct usbh_bus *bus)
{
uint32_t regval;
/* Disable OHCI interrupts */
OHCI_HCOR->hcintdis = OHCI_INT_SO | OHCI_INT_RD | OHCI_INT_UE | OHCI_INT_OC |
OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE;
for (uint8_t port = 0; port < g_ehci_hcd[bus->hcd.hcd_id].n_pcc; port++) {
regval = OHCI_HCOR->hcrhportsts[port];
regval &= ~OHCI_RHPORTST_PPS;
OHCI_HCOR->hcrhportsts[port] = regval;
}
return 0;
}
uint16_t ohci_get_frame_number(struct usbh_bus *bus)
{
return OHCI_HCOR->hcfmnumber;
}
int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf)
{
uint8_t nports;
uint8_t port;
uint32_t temp;
nports = g_ehci_hcd[bus->hcd.hcd_id].n_pcc;
port = setup->wIndex;
if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) {
switch (setup->bRequest) {
case HUB_REQUEST_CLEAR_FEATURE:
switch (setup->wValue) {
case HUB_FEATURE_HUB_C_LOCALPOWER:
break;
case HUB_FEATURE_HUB_C_OVERCURRENT:
break;
default:
return -USB_ERR_NOTSUPP;
}
break;
case HUB_REQUEST_SET_FEATURE:
switch (setup->wValue) {
case HUB_FEATURE_HUB_C_LOCALPOWER:
break;
case HUB_FEATURE_HUB_C_OVERCURRENT:
break;
default:
return -USB_ERR_NOTSUPP;
}
break;
case HUB_REQUEST_GET_DESCRIPTOR:
break;
case HUB_REQUEST_GET_STATUS:
memset(buf, 0, 4);
break;
default:
break;
}
} else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) {
switch (setup->bRequest) {
case HUB_REQUEST_CLEAR_FEATURE:
if (!port || port > nports) {
return -USB_ERR_INVAL;
}
switch (setup->wValue) {
case HUB_PORT_FEATURE_ENABLE:
break;
case HUB_PORT_FEATURE_SUSPEND:
case HUB_PORT_FEATURE_C_SUSPEND:
break;
case HUB_PORT_FEATURE_POWER:
break;
case HUB_PORT_FEATURE_C_CONNECTION:
OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_CSC;
break;
case HUB_PORT_FEATURE_C_ENABLE:
OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PESC;
break;
case HUB_PORT_FEATURE_C_OVER_CURREN:
OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_OCIC;
break;
case HUB_PORT_FEATURE_C_RESET:
OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PRSC;
break;
default:
return -USB_ERR_NOTSUPP;
}
break;
case HUB_REQUEST_SET_FEATURE:
if (!port || port > nports) {
return -USB_ERR_INVAL;
}
switch (setup->wValue) {
case HUB_PORT_FEATURE_SUSPEND:
break;
case HUB_PORT_FEATURE_POWER:
break;
case HUB_PORT_FEATURE_RESET:
OHCI_HCOR->hcrhportsts[port - 1] |= OHCI_RHPORTST_PRS;
while (OHCI_HCOR->hcrhportsts[port - 1] & OHCI_RHPORTST_PRS) {
}
break;
default:
return -USB_ERR_NOTSUPP;
}
break;
case HUB_REQUEST_GET_STATUS:
if (!port || port > nports) {
return -USB_ERR_INVAL;
}
temp = OHCI_HCOR->hcrhportsts[port - 1];
memcpy(buf, &temp, 4);
break;
default:
break;
}
}
return 0;
}
int ohci_submit_urb(struct usbh_urb *urb)
{
return -USB_ERR_NOTSUPP;
}
int ohci_kill_urb(struct usbh_urb *urb)
{
return -USB_ERR_NOTSUPP;
}
void OHCI_IRQHandler(uint8_t busid)
{
uint32_t usbsts;
struct usbh_bus *bus;
bus = &g_usbhost_bus[busid];
usbsts = OHCI_HCOR->hcintsts & OHCI_HCOR->hcinten;
OHCI_HCOR->hcintsts = usbsts;
if (usbsts & OHCI_INT_RHSC) {
for (int port = 0; port < CONFIG_USBHOST_MAX_RHPORTS; port++) {
uint32_t portsc = OHCI_HCOR->hcrhportsts[port];
if (portsc & OHCI_RHPORTST_CSC) {
if (OHCI_HCOR->hcrhsts & OHCI_RHSTATUS_DRWE) {
/* If DRWE is set, Connect Status Change indicates a remote wake-up event */
} else {
if (portsc & OHCI_RHPORTST_CCS) {
} else {
}
bus->hcd.roothub.int_buffer[0] |= (1 << (port + 1));
usbh_hub_thread_wakeup(&bus->hcd.roothub);
}
}
}
}
if (usbsts & OHCI_INT_WDH) {
}
}