📄 atmel_usba_udc.c
字号:
if (crq.crq.wLength != __constant_cpu_to_le16(0)) ep->state = DATA_STAGE_OUT; else ep->state = STATUS_STAGE_IN; } ret = -1; if (ep->index == 0) ret = handle_ep0_setup(udc, ep, &crq.crq); else { spin_unlock(&udc->lock); ret = udc->driver->setup(&udc->gadget, &crq.crq); spin_lock(&udc->lock); } DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", crq.crq.bRequestType, crq.crq.bRequest, le16_to_cpu(crq.crq.wLength), ep->state, ret); if (ret < 0) { /* Let the host know that we failed */ set_protocol_stall(udc, ep); } }}static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep){ struct usba_request *req; u32 epstatus; u32 epctrl; epstatus = usba_ep_readl(ep, STA); epctrl = usba_ep_readl(ep, CTL); DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", ep->ep.name, epstatus); while ((epctrl & USBA_TX_PK_RDY) && !(epstatus & USBA_TX_PK_RDY)) { DBG(DBG_BUS, "%s: TX PK ready\n", ep->ep.name); if (list_empty(&ep->queue)) { dev_warn(&udc->pdev->dev, "ep_irq: queue empty\n"); usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); return; } req = list_entry(ep->queue.next, struct usba_request, queue); if (req->using_dma) { /* Send a zero-length packet */ usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); list_del_init(&req->queue); submit_next_request(ep); request_complete(ep, req, 0); } else { if (req->submitted) next_fifo_transaction(ep, req); else submit_request(ep, req); if (req->last_transaction) { list_del_init(&req->queue); submit_next_request(ep); request_complete(ep, req, 0); } } epstatus = usba_ep_readl(ep, STA); epctrl = usba_ep_readl(ep, CTL); } if ((epstatus & epctrl) & USBA_RX_BK_RDY) { DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); receive_data(ep); usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); }}static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep){ struct usba_request *req; u32 status, control, pending; status = usba_dma_readl(ep, STATUS); control = usba_dma_readl(ep, CONTROL);#ifdef CONFIG_USB_GADGET_DEBUG_FS ep->last_dma_status = status;#endif pending = status & control; DBG(DBG_INT | DBG_DMA, "dma irq, s/%#08x, c/%#08x\n", status, control); if (status & USBA_DMA_CH_EN) { dev_err(&udc->pdev->dev, "DMA_CH_EN is set after transfer is finished!\n"); dev_err(&udc->pdev->dev, "status=%#08x, pending=%#08x, control=%#08x\n", status, pending, control); /* * try to pretend nothing happened. We might have to * do something here... */ } if (list_empty(&ep->queue)) /* Might happen if a reset comes along at the right moment */ return; if (pending & (USBA_DMA_END_TR_ST | USBA_DMA_END_BUF_ST)) { req = list_entry(ep->queue.next, struct usba_request, queue); usba_update_req(ep, req, status); list_del_init(&req->queue); submit_next_request(ep); request_complete(ep, req, 0); }}static irqreturn_t usba_udc_irq(int irq, void *devid){ struct usba_udc *udc = devid; u32 status; u32 dma_status; u32 ep_status; spin_lock(&udc->lock); status = usba_readl(udc, INT_STA); DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->suspend) { spin_unlock(&udc->lock); udc->driver->suspend(&udc->gadget); spin_lock(&udc->lock); } } if (status & USBA_WAKE_UP) { usba_writel(udc, INT_CLR, USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } if (status & USBA_END_OF_RESUME) { usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); DBG(DBG_BUS, "Resume detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->resume) { spin_unlock(&udc->lock); udc->driver->resume(&udc->gadget); spin_lock(&udc->lock); } } dma_status = USBA_BFEXT(DMA_INT, status); if (dma_status) { int i; for (i = 1; i < USBA_NR_ENDPOINTS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &usba_ep[i]); } ep_status = USBA_BFEXT(EPT_INT, status); if (ep_status) { int i; for (i = 0; i < USBA_NR_ENDPOINTS; i++) if (ep_status & (1 << i)) { if (ep_is_control(&usba_ep[i])) usba_control_irq(udc, &usba_ep[i]); else usba_ep_irq(udc, &usba_ep[i]); } } if (status & USBA_END_OF_RESET) { struct usba_ep *ep0; usba_writel(udc, INT_CLR, USBA_END_OF_RESET); reset_all_endpoints(udc); if (status & USBA_HIGH_SPEED) { DBG(DBG_BUS, "High-speed bus reset detected\n"); udc->gadget.speed = USB_SPEED_HIGH; } else { DBG(DBG_BUS, "Full-speed bus reset detected\n"); udc->gadget.speed = USB_SPEED_FULL; } ep0 = &usba_ep[0]; ep0->desc = &usba_ep0_desc; ep0->state = WAIT_FOR_SETUP; usba_ep_writel(ep0, CFG, (USBA_BF(EPT_SIZE, EP0_EPT_SIZE) | USBA_BF(EPT_TYPE, USBA_EPT_TYPE_CONTROL) | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); usba_ep_writel(ep0, CTL_ENB, USBA_EPT_ENABLE | USBA_RX_SETUP); usba_writel(udc, INT_ENB, (usba_readl(udc, INT_ENB) | USBA_BF(EPT_INT, 1) | USBA_DET_SUSPEND | USBA_END_OF_RESUME)); if (!(usba_ep_readl(ep0, CFG) & USBA_EPT_MAPPED)) dev_warn(&udc->pdev->dev, "WARNING: EP0 configuration is invalid!\n"); } spin_unlock(&udc->lock); return IRQ_HANDLED;}static irqreturn_t usba_vbus_irq(int irq, void *devid){ struct usba_udc *udc = devid; int vbus; /* debounce */ udelay(10); spin_lock(&udc->lock); /* May happen if Vbus pin toggles during probe() */ if (!udc->driver) goto out; vbus = gpio_get_value(udc->vbus_pin); if (vbus != udc->vbus_prev) { if (vbus) { usba_writel(udc, CTRL, USBA_EN_USBA); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } else { udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); usba_writel(udc, CTRL, 0); spin_unlock(&udc->lock); udc->driver->disconnect(&udc->gadget); spin_lock(&udc->lock); } udc->vbus_prev = vbus; }out: spin_unlock(&udc->lock); return IRQ_HANDLED;}int usb_gadget_register_driver(struct usb_gadget_driver *driver){ struct usba_udc *udc = &the_udc; unsigned long flags; int ret; if (!udc->pdev) return -ENODEV; spin_lock_irqsave(&udc->lock, flags); if (udc->driver) { spin_unlock_irqrestore(&udc->lock, flags); return -EBUSY; } udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); clk_enable(udc->pclk); clk_enable(udc->hclk); ret = driver->bind(&udc->gadget); if (ret) { DBG(DBG_ERR, "Could not bind to driver %s: error %d\n", driver->driver.name, ret); goto err_driver_bind; } DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); udc->vbus_prev = 0; if (udc->vbus_pin != -1) enable_irq(gpio_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ spin_lock_irqsave(&udc->lock, flags); if (vbus_is_present(udc) && udc->vbus_prev == 0) { usba_writel(udc, CTRL, USBA_EN_USBA); usba_writel(udc, INT_ENB, USBA_END_OF_RESET); } spin_unlock_irqrestore(&udc->lock, flags); return 0;err_driver_bind: udc->driver = NULL; udc->gadget.dev.driver = NULL; return ret;}EXPORT_SYMBOL(usb_gadget_register_driver);int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){ struct usba_udc *udc = &the_udc; unsigned long flags; if (!udc->pdev) return -ENODEV; if (driver != udc->driver) return -EINVAL; if (udc->vbus_pin != -1) disable_irq(gpio_to_irq(udc->vbus_pin)); spin_lock_irqsave(&udc->lock, flags); udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); spin_unlock_irqrestore(&udc->lock, flags); /* This will also disable the DP pullup */ usba_writel(udc, CTRL, 0); driver->unbind(&udc->gadget); udc->gadget.dev.driver = NULL; udc->driver = NULL; clk_disable(udc->hclk); clk_disable(udc->pclk); DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);static int __init usba_udc_probe(struct platform_device *pdev){ struct usba_platform_data *pdata = pdev->dev.platform_data; struct resource *regs, *fifo; struct clk *pclk, *hclk; struct usba_udc *udc = &the_udc; int irq, ret, i; regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); if (!regs || !fifo) return -ENXIO; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; pclk = clk_get(&pdev->dev, "pclk"); if (IS_ERR(pclk)) return PTR_ERR(pclk); hclk = clk_get(&pdev->dev, "hclk"); if (IS_ERR(hclk)) { ret = PTR_ERR(hclk); goto err_get_hclk; } udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; udc->vbus_pin = -1; ret = -ENOMEM; udc->regs = ioremap(regs->start, regs->end - regs->start + 1); if (!udc->regs) { dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); goto err_map_regs; } dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", (unsigned long)regs->start, udc->regs); udc->fifo = ioremap(fifo->start, fifo->end - fifo->start + 1); if (!udc->fifo) { dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); goto err_map_fifo; } dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", (unsigned long)fifo->start, udc->fifo); device_initialize(&udc->gadget.dev); udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; platform_set_drvdata(pdev, udc); /* Make sure we start from a clean slate */ clk_enable(pclk); usba_writel(udc, CTRL, 0); clk_disable(pclk); INIT_LIST_HEAD(&usba_ep[0].ep.ep_list); usba_ep[0].ep_regs = udc->regs + USBA_EPT_BASE(0); usba_ep[0].dma_regs = udc->regs + USBA_DMA_BASE(0); usba_ep[0].fifo = udc->fifo + USBA_FIFO_BASE(0); for (i = 1; i < ARRAY_SIZE(usba_ep); i++) { struct usba_ep *ep = &usba_ep[i]; ep->ep_regs = udc->regs + USBA_EPT_BASE(i); ep->dma_regs = udc->regs + USBA_DMA_BASE(i); ep->fifo = udc->fifo + USBA_FIFO_BASE(i); list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); } ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc); if (ret) { dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n", irq, ret); goto err_request_irq; } udc->irq = irq; ret = device_add(&udc->gadget.dev); if (ret) { dev_dbg(&pdev->dev, "Could not add gadget: %d\n", ret); goto err_device_add; } if (pdata && pdata->vbus_pin != GPIO_PIN_NONE) { if (!gpio_request(pdata->vbus_pin, "atmel_usba_udc")) { udc->vbus_pin = pdata->vbus_pin; ret = request_irq(gpio_to_irq(udc->vbus_pin), usba_vbus_irq, 0, "atmel_usba_udc", udc); if (ret) { gpio_free(udc->vbus_pin); udc->vbus_pin = -1; dev_warn(&udc->pdev->dev, "failed to request vbus irq; " "assuming always on\n"); } else { disable_irq(gpio_to_irq(udc->vbus_pin)); } } } usba_init_debugfs(udc); for (i = 1; i < ARRAY_SIZE(usba_ep); i++) usba_ep_init_debugfs(udc, &usba_ep[i]); return 0;err_device_add: free_irq(irq, udc);err_request_irq: iounmap(udc->fifo);err_map_fifo: iounmap(udc->regs);err_map_regs: clk_put(hclk);err_get_hclk: clk_put(pclk); platform_set_drvdata(pdev, NULL); return ret;}static int __exit usba_udc_remove(struct platform_device *pdev){ struct usba_udc *udc; int i; udc = platform_get_drvdata(pdev); for (i = 1; i < ARRAY_SIZE(usba_ep); i++) usba_ep_cleanup_debugfs(&usba_ep[i]); usba_cleanup_debugfs(udc); if (udc->vbus_pin != -1) gpio_free(udc->vbus_pin); free_irq(udc->irq, udc); iounmap(udc->fifo); iounmap(udc->regs); clk_put(udc->hclk); clk_put(udc->pclk); device_unregister(&udc->gadget.dev); return 0;}static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { .name = "atmel_usba_udc", },};static int __init udc_init(void){ return platform_driver_probe(&udc_driver, usba_udc_probe);}module_init(udc_init);static void __exit udc_exit(void){ platform_driver_unregister(&udc_driver);}module_exit(udc_exit);MODULE_DESCRIPTION("Atmel USBA UDC driver");MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -