Copy xhci functions first. We should redevelop xhci functions step by step

This commit is contained in:
xj 2024-06-17 19:01:59 -07:00
parent 01a92a269c
commit 303737e590
2 changed files with 3049 additions and 0 deletions

View File

@ -91,4 +91,448 @@ static inline struct xhci_host* xhci_get_inst_of_port(struct usbh_hubport *hport
return (struct xhci_host*)usb->priv; return (struct xhci_host*)usb->priv;
} }
static struct xhci_host xhci_host[CONFIG_USBHOST_XHCI_NUM];
/* xhci hardware init */
int usb_hc_init(uint32_t id)
{
USB_ASSERT(id < CONFIG_USBHOST_XHCI_NUM);
int rc = 0;
struct usbh_bus *bus = usbh_get_bus_of_index(id);
USB_ASSERT(bus);
struct xhci_host *xhci = &(xhci_host[id]);
size_t flag = usb_osal_enter_critical_section(); /* no interrupt when init hc */
usb_hc_low_level_init(id); /* set gic and memp */
memset(xhci, 0, sizeof(*xhci));
xhci->bus = bus;
bus->priv = xhci;
if (rc = xhci_probe(xhci, usb_hc_get_register_base(id)) != 0) {
goto err_open;
}
if (rc = xhci_open(xhci) != 0 ) {
goto err_open;
}
err_open:
usb_osal_leave_critical_section(flag);
return rc;
}
int usbh_roothub_control(struct usbh_bus *usb, struct usb_setup_packet *setup, uint8_t *buf)
{
uint8_t nports;
uint8_t port;
uint32_t portsc;
uint32_t status;
int ret = 0;
struct xhci_host *xhci = usb->priv;
nports = CONFIG_USBHOST_MAX_RHPORTS;
port = setup->wIndex;
/*
bmRequestType bit[4:0], define whether the request is directed to the device (0000b),
specific interface (00001b), endpoint (00010b), or other element (00011b)
bRequest, identifies the request
wValue, pass the request-specific info to the device
*/
if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) /* request is directed to device */
{
switch (setup->bRequest)
{
case HUB_REQUEST_CLEAR_FEATURE: /* disable the feature */
switch (setup->wValue)
{
case HUB_FEATURE_HUB_C_LOCALPOWER:
USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n");
break;
case HUB_FEATURE_HUB_C_OVERCURRENT:
USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n");
break;
default:
return -EPIPE;
}
break;
case HUB_REQUEST_SET_FEATURE: /* set a value reported in the hub status */
switch (setup->wValue)
{
case HUB_FEATURE_HUB_C_LOCALPOWER:
USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n");
break;
case HUB_FEATURE_HUB_C_OVERCURRENT:
USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n");
break;
default:
return -EPIPE;
}
break;
case HUB_REQUEST_GET_DESCRIPTOR:
USB_LOG_ERR("HUB_REQUEST_GET_DESCRIPTOR not implmented.\n");
break;
case HUB_REQUEST_GET_STATUS:
USB_ASSERT(buf);
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 -EPIPE;
}
portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) );
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:
portsc |= XHCI_PORTSC_CSC;
break;
case HUB_PORT_FEATURE_C_ENABLE:
portsc |= XHCI_PORTSC_PEC;
break;
case HUB_PORT_FEATURE_C_OVER_CURREN:
break;
case HUB_PORT_FEATURE_C_RESET:
break;
default:
return -EPIPE;
}
uint32_t pclear = portsc & XHCI_PORTSC_RW_MASK;
/* clear port status */
writel(pclear, xhci->op + XHCI_OP_PORTSC ( port ));
break;
case HUB_REQUEST_SET_FEATURE:
if (!port || port > nports)
{
return -EPIPE;
}
switch (setup->wValue)
{
case HUB_PORT_FEATURE_SUSPEND:
break;
case HUB_PORT_FEATURE_POWER:
break;
case HUB_PORT_FEATURE_RESET:
ret = xhci_port_enable(xhci, port);
break;
default:
return -EPIPE;
}
break;
case HUB_REQUEST_GET_STATUS:
if (!port || port > nports)
{
return -EPIPE;
}
portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) );
status = 0;
if (portsc & XHCI_PORTSC_CSC)
{
/* Port connection status changed */
status |= (1 << HUB_PORT_FEATURE_C_CONNECTION);
/* always clear all the status change bits */
uint32_t temp = portsc & ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE );
writel ( temp, xhci->op + XHCI_OP_PORTSC ( port ) );
}
if (portsc & XHCI_PORTSC_PEC)
{
/* Port enabled status changed */
status |= (1 << HUB_PORT_FEATURE_C_ENABLE);
}
if (portsc & XHCI_PORTSC_OCC)
{
/* Port status changed due to over-current */
status |= (1 << HUB_PORT_FEATURE_C_OVER_CURREN);
}
if (portsc & XHCI_PORTSC_CCS)
{
/* Port connected */
status |= (1 << HUB_PORT_FEATURE_CONNECTION);
}
if (portsc & XHCI_PORTSC_PED)
{
/* Port enabled */
status |= (1 << HUB_PORT_FEATURE_ENABLE);
const unsigned int speed = xhci_root_speed(xhci, port);
USB_LOG_DBG("Port-%d speed = %d \r\n", port, speed);
if (speed == USB_SPEED_LOW)
{
status |= (1 << HUB_PORT_FEATURE_LOWSPEED);
}
else if (speed == USB_SPEED_HIGH)
{
status |= (1 << HUB_PORT_FEATURE_HIGHSPEED);
}
/* else is full-speed */
}
if (portsc & XHCI_PORTSC_OCA)
{
/* Over-current condition */
status |= (1 << HUB_PORT_FEATURE_OVERCURRENT);
}
if (portsc & XHCI_PORTSC_PR)
{
/* Reset is in progress */
status |= (1 << HUB_PORT_FEATURE_RESET);
}
if (portsc & XHCI_PORTSC_PP)
{
/* Port is not power off */
status |= (1 << HUB_PORT_FEATURE_POWER);
}
memcpy(buf, &status, 4);
break;
default:
break;
}
}
return 0;
}
uint8_t usbh_get_port_speed(struct usbh_hub *hub, const uint8_t port)
{
USB_ASSERT(hub);
struct usbh_bus *usb = hub->usb;
USB_ASSERT(usb && usb->priv);
struct xhci_host *xhci = usb->priv;
if (hub->is_roothub) {
return xhci_root_speed(xhci, port);
} else {
struct usbh_hubport *roothub_port = usbh_root_hub_port(&(hub->child[port]));
/* TODO, hanlde TT for low-speed device on high-speed hub, but it doesn't matter, since
we haven't well handle hub yet */
USB_ASSERT(roothub_port);
return xhci_root_speed(xhci, roothub_port->port);
}
}
int usbh_ep_pipe_reconfigure(struct usbh_bus *usb, usbh_pipe_t pipe, uint8_t dev_addr, uint8_t mtu, uint8_t speed)
{
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
size_t old_mtu = ppipe->mtu;
int rc = 0;
if (mtu != old_mtu) {
ppipe->mtu = mtu;
rc = xhci_endpoint_mtu(ppipe);
}
return rc;
}
int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg)
{
int rc = 0;
int slot_id = 0;
struct xhci_host *xhci = xhci_get_inst_of_port(ep_cfg->hport);
struct usbh_hubport *hport = ep_cfg->hport;
struct xhci_endpoint *ppipe = usb_align(XHCI_RING_SIZE, sizeof(struct xhci_endpoint));
struct xhci_slot *slot;
if (NULL == ppipe) {
return -ENOMEM;
}
memset(ppipe, 0, sizeof(struct xhci_endpoint));
ppipe->waitsem = usb_osal_sem_create(0);
ppipe->waiter = false;
ppipe->urb = NULL;
ppipe->hport = hport;
ppipe->address = ep_cfg->ep_addr;
ppipe->mtu = ep_cfg->ep_mps;
ppipe->interval = ep_cfg->ep_interval;
ppipe->ep_type = ep_cfg->ep_type;
ppipe->burst = 0U;
if (ppipe->address == 0) { /* if try to allocate ctrl ep, open device first */
USB_LOG_DBG("allocate device for port-%d \r\n", hport->port);
rc = xhci_device_open(xhci, ppipe, &slot_id);
if (rc) {
goto failed;
}
slot = xhci->slot[slot_id];
USB_ASSERT(slot);
rc = xhci_ctrl_endpoint_open(xhci, slot, ppipe);
if (rc) {
goto failed;
}
rc = xhci_device_address(xhci, slot, ppipe);
if (rc) {
goto failed;
}
} else {
slot_id = hport->dev_addr;
slot = xhci->slot[slot_id];
rc = xhci_work_endpoint_open(xhci, slot, ppipe);
if (rc) {
goto failed;
}
}
*pipe = (usbh_pipe_t)ppipe;
return rc;
failed:
usb_free(ppipe);
*pipe = NULL;
return rc;
}
int usbh_get_xhci_devaddr(usbh_pipe_t *pipe)
{
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
USB_ASSERT(ppipe && (ppipe->slot));
return ppipe->slot->id;
}
int usbh_pipe_free(usbh_pipe_t pipe)
{
int ret = 0;
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
if (!ppipe) {
return -EINVAL;
}
struct usbh_urb *urb = ppipe->urb;
if (urb) {
usbh_kill_urb(urb);
}
size_t flags = usb_osal_enter_critical_section();
xhci_endpoint_close(ppipe);
usb_osal_leave_critical_section(flags);
return 0;
}
int usbh_submit_urb(struct usbh_urb *urb)
{
int ret = 0;
if (!urb || !urb->pipe) {
return -EINVAL;
}
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)urb->pipe;
struct xhci_host *xhci = ppipe->xhci;
struct usb_setup_packet *setup = urb->setup;
size_t flags = usb_osal_enter_critical_section();
urb->errorcode = -EBUSY;
urb->actual_length = 0U;
ppipe->urb = urb;
ppipe->timeout = urb->timeout;
if (ppipe->timeout > 0) {
ppipe->waiter = true;
} else {
ppipe->waiter = false;
}
usb_osal_leave_critical_section(flags);
switch (ppipe->ep_type) {
case USB_ENDPOINT_TYPE_CONTROL:
USB_ASSERT(setup);
if (setup->bRequest == USB_REQUEST_SET_ADDRESS) {
/* Set address command sent during xhci_alloc_pipe. */
goto skip_req;
}
USB_LOG_DBG("%s request-%d.\n", __func__, setup->bRequest);
xhci_endpoint_message(ppipe, setup, urb->transfer_buffer, urb->transfer_buffer_length);
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
case USB_ENDPOINT_TYPE_BULK:
xhci_endpoint_stream(ppipe, urb->transfer_buffer, urb->transfer_buffer_length);
break;
default:
USB_ASSERT(0U);
break;
}
/* wait all ring handled by xHc */
int cc = xhci_event_wait(xhci, ppipe, &ppipe->reqs);
if ((cc != XHCI_CMPLT_SUCCESS) &&
!((cc == XHCI_CMPLT_TIMEOUT) && (ppipe->ep_type == USB_ENDPOINT_TYPE_INTERRUPT)))
{
/* ignore transfer timeout for interrupt type */
USB_LOG_ERR("%s: xfer failed (cc %d).\n", __func__, cc);
ret = -1;
urb->errorcode = cc;
goto errout_timeout;
}
skip_req:
errout_timeout:
/* Timeout will run here */
usbh_kill_urb(urb);
return ret;
}
int usbh_kill_urb(struct usbh_urb *urb)
{
return 0;
}
void USBH_IRQHandler(void *param)
{
USB_ASSERT(param);
struct usbh_bus *bus = (struct usbh_bus *)param;
struct xhci_host *xhci = bus->priv;
USB_ASSERT(xhci);
uint32_t usbsts;
uint32_t runtime;
/* clear interrupt status */
usbsts = readl ( xhci->op + XHCI_OP_USBSTS );
usbsts |= XHCI_USBSTS_EINT;
writel(usbsts, xhci->op + XHCI_OP_USBSTS);
/* ack interrupt */
runtime = readl(xhci->run + XHCI_RUN_IR_IMAN ( 0 ));
runtime |= XHCI_RUN_IR_IMAN_IP;
writel (runtime, xhci->run + XHCI_RUN_IR_IMAN ( 0 ));
(void)xhci_event_process(xhci);
}