📄 fsl_usb2_udc.c
字号:
status = -EPIPE; /* FIXME: continue with next queued TD? */ break; } if (errors & DTD_STATUS_DATA_BUFF_ERR) { VDBG("Transfer overflow"); status = -EPROTO; break; } else if (errors & DTD_STATUS_TRANSACTION_ERR) { VDBG("ISO error"); status = -EILSEQ; break; } else ERR("Unknown error has occured (0x%x)!\r\n", errors); } else if (le32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { VDBG("Request not complete"); status = REQ_UNCOMPLETE; return status; } else if (remaining_length) { if (direction) { VDBG("Transmit dTD remaining length not zero"); status = -EPROTO; break; } else { td_complete++; break; } } else { td_complete++; VDBG("dTD transmitted successful "); } if (j != curr_req->dtd_count - 1) curr_td = (struct ep_td_struct *)curr_td->next_td_virt; } if (status) return status; curr_req->req.actual = actual; return 0;}/* Process a DTD completion interrupt */static void dtd_complete_irq(struct fsl_udc *udc){ u32 bit_pos; int i, ep_num, direction, bit_mask, status; struct fsl_ep *curr_ep; struct fsl_req *curr_req, *temp_req; /* Clear the bits in the register */ bit_pos = fsl_readl(&dr_regs->endptcomplete); fsl_writel(bit_pos, &dr_regs->endptcomplete); if (!bit_pos) return; for (i = 0; i < udc->max_ep * 2; i++) { ep_num = i >> 1; direction = i % 2; bit_mask = 1 << (ep_num + 16 * direction); if (!(bit_pos & bit_mask)) continue; curr_ep = get_ep_by_pipe(udc, i); /* If the ep is configured */ if (curr_ep->name == NULL) { WARN("Invalid EP?"); continue; } /* process the req queue until an uncomplete request */ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, queue) { status = process_ep_req(udc, i, curr_req); VDBG("status of process_ep_req= %d, ep = %d", status, ep_num); if (status == REQ_UNCOMPLETE) break; /* write back status to req */ curr_req->req.status = status; if (ep_num == 0) { ep0_req_complete(udc, curr_ep, curr_req); break; } else done(curr_ep, curr_req, status); } }}/* Process a port change interrupt */static void port_change_irq(struct fsl_udc *udc){ u32 speed; if (udc->bus_reset) udc->bus_reset = 0; /* Bus resetting is finished */ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { /* Get the speed */ speed = (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SPEED_MASK); switch (speed) { case PORTSCX_PORT_SPEED_HIGH: udc->gadget.speed = USB_SPEED_HIGH; break; case PORTSCX_PORT_SPEED_FULL: udc->gadget.speed = USB_SPEED_FULL; break; case PORTSCX_PORT_SPEED_LOW: udc->gadget.speed = USB_SPEED_LOW; break; default: udc->gadget.speed = USB_SPEED_UNKNOWN; break; } } /* Update USB state */ if (!udc->resume_state) udc->usb_state = USB_STATE_DEFAULT;}/* Process suspend interrupt */static void suspend_irq(struct fsl_udc *udc){ udc->resume_state = udc->usb_state; udc->usb_state = USB_STATE_SUSPENDED; /* report suspend to the driver, serial.c does not support this */ if (udc->driver->suspend) udc->driver->suspend(&udc->gadget);}static void bus_resume(struct fsl_udc *udc){ udc->usb_state = udc->resume_state; udc->resume_state = 0; /* report resume to the driver, serial.c does not support this */ if (udc->driver->resume) udc->driver->resume(&udc->gadget);}/* Clear up all ep queues */static int reset_queues(struct fsl_udc *udc){ u8 pipe; for (pipe = 0; pipe < udc->max_pipes; pipe++) udc_reset_ep_queue(udc, pipe); /* report disconnect; the driver is already quiesced */ udc->driver->disconnect(&udc->gadget); return 0;}/* Process reset interrupt */static void reset_irq(struct fsl_udc *udc){ u32 temp; unsigned long timeout; /* Clear the device address */ temp = fsl_readl(&dr_regs->deviceaddr); fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr); udc->device_address = 0; /* Clear usb state */ udc->resume_state = 0; udc->ep0_dir = 0; udc->ep0_state = WAIT_FOR_SETUP; udc->remote_wakeup = 0; /* default to 0 on reset */ udc->gadget.b_hnp_enable = 0; udc->gadget.a_hnp_support = 0; udc->gadget.a_alt_hnp_support = 0; /* Clear all the setup token semaphores */ temp = fsl_readl(&dr_regs->endptsetupstat); fsl_writel(temp, &dr_regs->endptsetupstat); /* Clear all the endpoint complete status bits */ temp = fsl_readl(&dr_regs->endptcomplete); fsl_writel(temp, &dr_regs->endptcomplete); timeout = jiffies + 100; while (fsl_readl(&dr_regs->endpointprime)) { /* Wait until all endptprime bits cleared */ if (time_after(jiffies, timeout)) { ERR("Timeout for reset\n"); break; } cpu_relax(); } /* Write 1s to the flush register */ fsl_writel(0xffffffff, &dr_regs->endptflush); if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { VDBG("Bus reset"); /* Bus is reseting */ udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue * head and TR Queue */ reset_queues(udc); udc->usb_state = USB_STATE_DEFAULT; } else { VDBG("Controller reset"); /* initialize usb hw reg except for regs for EP, not * touch usbintr reg */ dr_controller_setup(udc); /* Reset all internal used Queues */ reset_queues(udc); ep0_setup(udc); /* Enable DR IRQ reg, Set Run bit, change udc state */ dr_controller_run(udc); udc->usb_state = USB_STATE_ATTACHED; }}/* * USB device controller interrupt handler */static irqreturn_t fsl_udc_irq(int irq, void *_udc){ struct fsl_udc *udc = _udc; u32 irq_src; irqreturn_t status = IRQ_NONE; unsigned long flags; /* Disable ISR for OTG host mode */ if (udc->stopped) return IRQ_NONE; spin_lock_irqsave(&udc->lock, flags); irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr); /* Clear notification bits */ fsl_writel(irq_src, &dr_regs->usbsts); /* VDBG("irq_src [0x%8x]", irq_src); */ /* Need to resume? */ if (udc->usb_state == USB_STATE_SUSPENDED) if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0) bus_resume(udc); /* USB Interrupt */ if (irq_src & USB_STS_INT) { VDBG("Packet int"); /* Setup package, we only support ep0 as control ep */ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { tripwire_handler(udc, 0, (u8 *) (&udc->local_setup_buff)); setup_received_irq(udc, &udc->local_setup_buff); status = IRQ_HANDLED; } /* completion of dtd */ if (fsl_readl(&dr_regs->endptcomplete)) { dtd_complete_irq(udc); status = IRQ_HANDLED; } } /* SOF (for ISO transfer) */ if (irq_src & USB_STS_SOF) { status = IRQ_HANDLED; } /* Port Change */ if (irq_src & USB_STS_PORT_CHANGE) { port_change_irq(udc); status = IRQ_HANDLED; } /* Reset Received */ if (irq_src & USB_STS_RESET) { reset_irq(udc); status = IRQ_HANDLED; } /* Sleep Enable (Suspend) */ if (irq_src & USB_STS_SUSPEND) { suspend_irq(udc); status = IRQ_HANDLED; } if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { VDBG("Error IRQ %x ", irq_src); } spin_unlock_irqrestore(&udc->lock, flags); return status;}/*----------------------------------------------------------------* * Hook to gadget drivers * Called by initialization code of gadget drivers*----------------------------------------------------------------*/int usb_gadget_register_driver(struct usb_gadget_driver *driver){ int retval = -ENODEV; unsigned long flags = 0; if (!udc_controller) return -ENODEV; if (!driver || (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) || !driver->bind || !driver->disconnect || !driver->setup) return -EINVAL; if (udc_controller->driver) return -EBUSY; /* lock is needed but whether should use this lock or another */ spin_lock_irqsave(&udc_controller->lock, flags); driver->driver.bus = 0; /* hook up the driver */ udc_controller->driver = driver; udc_controller->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc_controller->lock, flags); /* bind udc driver to gadget driver */ retval = driver->bind(&udc_controller->gadget); if (retval) { VDBG("bind to %s --> %d", driver->driver.name, retval); udc_controller->gadget.dev.driver = 0; udc_controller->driver = 0; goto out; } /* Enable DR IRQ reg and Set usbcmd reg Run bit */ dr_controller_run(udc_controller); udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_state = WAIT_FOR_SETUP; udc_controller->ep0_dir = 0; printk(KERN_INFO "%s: bind to driver %s \n", udc_controller->gadget.name, driver->driver.name);out: if (retval) printk("retval %d \n", retval); return retval;}EXPORT_SYMBOL(usb_gadget_register_driver);/* Disconnect from gadget driver */int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){ struct fsl_ep *loop_ep; unsigned long flags; if (!udc_controller) return -ENODEV; if (!driver || driver != udc_controller->driver || !driver->unbind) return -EINVAL; if (udc_controller->transceiver) (void)otg_set_peripheral(udc_controller->transceiver, 0); /* stop DR, disable intr */ dr_controller_stop(udc_controller); /* in fact, no needed */ udc_controller->usb_state = USB_STATE_ATTACHED; udc_controller->ep0_state = WAIT_FOR_SETUP; udc_controller->ep0_dir = 0; /* stand operation */ spin_lock_irqsave(&udc_controller->lock, flags); udc_controller->gadget.speed = USB_SPEED_UNKNOWN; nuke(&udc_controller->eps[0], -ESHUTDOWN); list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, ep.ep_list) nuke(loop_ep, -ESHUTDOWN); spin_unlock_irqrestore(&udc_controller->lock, flags); /* unbind gadget and unhook driver. */ driver->unbind(&udc_controller->gadget); udc_controller->gadget.dev.driver = 0; udc_controller->driver = 0; printk("unregistered gadget driver '%s'\r\n", driver->driver.name); return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*------------------------------------------------------------------------- PROC File System Support-------------------------------------------------------------------------*/#ifdef CONFIG_USB_GADGET_DEBUG_FILES#include <linux/seq_file.h>static const char proc_filename[] = "driver/fsl_usb2_udc";static int fsl_proc_read(char *page, char **start, off_t off, int count, int *eof, void *_dev){ char *buf = page; char *next = buf; unsigned size = count; unsigned long flags; int t, i; u32 tmp_reg; struct fsl_ep *ep = NULL; struct fsl_req *req; struct fsl_udc *udc = udc_controller; if (off != 0) return 0; spin_lock_irqsave(&udc->lock, flags); /* ------basic driver infomation ---- */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\n" "Gadget driver: %s\n\n", driver_name, DRIVER_VERSION, udc->driver ? udc->driver->driver.name : "(none)"); size -= t; next += t; /* ------ DR Registers ----- */ tmp_reg = fsl_readl(&dr_regs->usbcmd); t = scnprintf(next, size, "USBCMD reg:\n" "SetupTW: %d\n" "Run/Stop: %s\n\n", (tmp_reg & USB_CMD_SUTW) ? 1 : 0, (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop"); size -= t; next += t; tmp_reg = fsl_readl(&dr_regs->usbsts); t = scnprintf(next, size, "USB Status Reg:\n" "Dr Suspend: %d" "Reset Received: %d" "System Error: %s" "USB Error Interrupt: %s\n\n", (tmp_reg & USB_STS_SUSPEND) ? 1 : 0, (tmp_reg & USB_STS_RESET) ? 1 : 0, (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal", (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err"); size -= t; next += t; tmp_reg = fsl_readl(&dr_regs->usbintr); t = scnprintf(next, size, "USB Intrrupt Enable Reg:\n" "Sleep Enable: %d" "SOF Received Enable: %d" "Reset Enable: %d\n" "System Error Enable: %d" "Port Change Dectected Enable: %d\n" "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n", (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0, (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0, (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0, (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0, (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0, (tmp_reg & USB_INTR_INT_EN) ? 1 : 0); size -= t; next += t; tmp_reg = fsl_readl(&dr_regs->frindex); t = scnprintf(next, size, "USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -