📄 at91_udc.c
字号:
break; /* USB reset irq: not maskable */ if (status & AT91_UDP_ENDBUSRES) { at91_udp_write(udc, AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS); at91_udp_write(udc, AT91_UDP_IER, MINIMUS_INTERRUPTUS); /* Atmel code clears this irq twice */ at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES); VDBG("end bus reset\n"); udc->addr = 0; stop_activity(udc); /* enable ep0 */ at91_udp_write(udc, AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL); udc->gadget.speed = USB_SPEED_FULL; udc->suspended = 0; at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_EP(0)); /* * NOTE: this driver keeps clocks off unless the * USB host is present. That saves power, but for * boards that don't support VBUS detection, both * clocks need to be active most of the time. */ /* host initiated suspend (3+ms bus idle) */ } else if (status & AT91_UDP_RXSUSP) { at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP); // VDBG("bus suspend\n"); if (udc->suspended) continue; udc->suspended = 1; /* * NOTE: when suspending a VBUS-powered device, the * gadget driver should switch into slow clock mode * and then into standby to avoid drawing more than * 500uA power (2500uA for some high-power configs). */ if (udc->driver && udc->driver->suspend) udc->driver->suspend(&udc->gadget); /* host initiated resume */ } else if (status & AT91_UDP_RXRSM) { at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); // VDBG("bus resume\n"); if (!udc->suspended) continue; udc->suspended = 0; /* * NOTE: for a VBUS-powered device, the gadget driver * would normally want to switch out of slow clock * mode into normal mode. */ if (udc->driver && udc->driver->resume) udc->driver->resume(&udc->gadget); /* endpoint IRQs are cleared by handling them */ } else { int i; unsigned mask = 1; struct at91_ep *ep = &udc->ep[1]; if (status & mask) handle_ep0(udc); for (i = 1; i < NUM_ENDPOINTS; i++) { mask <<= 1; if (status & mask) handle_ep(ep); ep++; } } } return IRQ_HANDLED;}/*-------------------------------------------------------------------------*/static void nop_release(struct device *dev){ /* nothing to free */}static struct at91_udc controller = { .gadget = { .ops = &at91_udc_ops, .ep0 = &controller.ep[0].ep, .name = driver_name, .dev = { .bus_id = "gadget", .release = nop_release, } }, .ep[0] = { .ep = { .name = ep0name, .ops = &at91_ep_ops, }, .udc = &controller, .maxpacket = 8, .int_mask = 1 << 0, }, .ep[1] = { .ep = { .name = "ep1", .ops = &at91_ep_ops, }, .udc = &controller, .is_pingpong = 1, .maxpacket = 64, .int_mask = 1 << 1, }, .ep[2] = { .ep = { .name = "ep2", .ops = &at91_ep_ops, }, .udc = &controller, .is_pingpong = 1, .maxpacket = 64, .int_mask = 1 << 2, }, .ep[3] = { .ep = { /* could actually do bulk too */ .name = "ep3-int", .ops = &at91_ep_ops, }, .udc = &controller, .maxpacket = 8, .int_mask = 1 << 3, }, .ep[4] = { .ep = { .name = "ep4", .ops = &at91_ep_ops, }, .udc = &controller, .is_pingpong = 1, .maxpacket = 256, .int_mask = 1 << 4, }, .ep[5] = { .ep = { .name = "ep5", .ops = &at91_ep_ops, }, .udc = &controller, .is_pingpong = 1, .maxpacket = 256, .int_mask = 1 << 5, }, /* ep6 and ep7 are also reserved (custom silicon might use them) */};static irqreturn_t at91_vbus_irq(int irq, void *_udc){ struct at91_udc *udc = _udc; unsigned value; /* vbus needs at least brief debouncing */ udelay(10); value = at91_get_gpio_value(udc->board.vbus_pin); if (value != udc->vbus) at91_vbus_session(&udc->gadget, value); return IRQ_HANDLED;}int usb_gadget_register_driver (struct usb_gadget_driver *driver){ struct at91_udc *udc = &controller; int retval; if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->setup) { DBG("bad parameter.\n"); return -EINVAL; } if (udc->driver) { DBG("UDC already has a gadget driver\n"); return -EBUSY; } udc->driver = driver; udc->gadget.dev.driver = &driver->driver; udc->gadget.dev.driver_data = &driver->driver; udc->enabled = 1; udc->selfpowered = 1; retval = driver->bind(&udc->gadget); if (retval) { DBG("driver->bind() returned %d\n", retval); udc->driver = NULL; udc->gadget.dev.driver = NULL; udc->gadget.dev.driver_data = NULL; udc->enabled = 0; udc->selfpowered = 0; return retval; } local_irq_disable(); pullup(udc, 1); local_irq_enable(); DBG("bound to %s\n", driver->driver.name); return 0;}EXPORT_SYMBOL (usb_gadget_register_driver);int usb_gadget_unregister_driver (struct usb_gadget_driver *driver){ struct at91_udc *udc = &controller; if (!driver || driver != udc->driver || !driver->unbind) return -EINVAL; local_irq_disable(); udc->enabled = 0; at91_udp_write(udc, AT91_UDP_IDR, ~0); pullup(udc, 0); local_irq_enable(); driver->unbind(&udc->gadget); udc->driver = NULL; DBG("unbound from %s\n", driver->driver.name); return 0;}EXPORT_SYMBOL (usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*/static void at91udc_shutdown(struct platform_device *dev){ /* force disconnect on reboot */ pullup(platform_get_drvdata(dev), 0);}static int __init at91udc_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct at91_udc *udc; int retval; struct resource *res; if (!dev->platform_data) { /* small (so we copy it) but critical! */ DBG("missing platform_data\n"); return -ENODEV; } if (pdev->num_resources != 2) { DBG("invalid num_resources"); return -ENODEV; } if ((pdev->resource[0].flags != IORESOURCE_MEM) || (pdev->resource[1].flags != IORESOURCE_IRQ)) { DBG("invalid resource type"); return -ENODEV; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; if (!request_mem_region(res->start, res->end - res->start + 1, driver_name)) { DBG("someone's using UDC memory\n"); return -EBUSY; } /* init software state */ udc = &controller; udc->gadget.dev.parent = dev; udc->board = *(struct at91_udc_data *) dev->platform_data; udc->pdev = pdev; udc->enabled = 0; udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1); if (!udc->udp_baseaddr) { release_mem_region(res->start, res->end - res->start + 1); return -ENOMEM; } udc_reinit(udc); /* get interface and function clocks */ udc->iclk = clk_get(dev, "udc_clk"); udc->fclk = clk_get(dev, "udpck"); if (IS_ERR(udc->iclk) || IS_ERR(udc->fclk)) { DBG("clocks missing\n"); retval = -ENODEV; goto fail0; } retval = device_register(&udc->gadget.dev); if (retval < 0) goto fail0; /* don't do anything until we have both gadget driver and VBUS */ clk_enable(udc->iclk); at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff); /* Clear all pending interrupts - UDP may be used by bootloader. */ at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff); clk_disable(udc->iclk); /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); if (request_irq(udc->udp_irq, at91_udc_irq, IRQF_DISABLED, driver_name, udc)) { DBG("request irq %d failed\n", udc->udp_irq); retval = -EBUSY; goto fail1; } if (udc->board.vbus_pin > 0) { /* * Get the initial state of VBUS - we cannot expect * a pending interrupt. */ udc->vbus = at91_get_gpio_value(udc->board.vbus_pin); if (request_irq(udc->board.vbus_pin, at91_vbus_irq, IRQF_DISABLED, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); free_irq(udc->udp_irq, udc); retval = -EBUSY; goto fail1; } } else { DBG("no VBUS detection, assuming always-on\n"); udc->vbus = 1; } dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); create_debug_file(udc); INFO("%s version %s\n", driver_name, DRIVER_VERSION); return 0;fail1: device_unregister(&udc->gadget.dev);fail0: release_mem_region(res->start, res->end - res->start + 1); DBG("%s probe failed, %d\n", driver_name, retval); return retval;}static int __exit at91udc_remove(struct platform_device *pdev){ struct at91_udc *udc = platform_get_drvdata(pdev); struct resource *res; DBG("remove\n"); if (udc->driver) return -EBUSY; pullup(udc, 0); device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); if (udc->board.vbus_pin > 0) free_irq(udc->board.vbus_pin, udc); free_irq(udc->udp_irq, udc); device_unregister(&udc->gadget.dev); iounmap(udc->udp_baseaddr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, res->end - res->start + 1); clk_put(udc->iclk); clk_put(udc->fclk); return 0;}#ifdef CONFIG_PMstatic int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg){ struct at91_udc *udc = platform_get_drvdata(pdev); int wake = udc->driver && device_may_wakeup(&pdev->dev); /* Unless we can act normally to the host (letting it wake us up * whenever it has work for us) force disconnect. Wakeup requires * PLLB for USB events (signaling for reset, wakeup, or incoming * tokens) and VBUS irqs (on systems which support them). */ if ((!udc->suspended && udc->addr) || !wake || at91_suspend_entering_slow_clock()) { pullup(udc, 0); wake = 0; } else enable_irq_wake(udc->udp_irq); udc->active_suspend = wake; if (udc->board.vbus_pin > 0 && wake) enable_irq_wake(udc->board.vbus_pin); return 0;}static int at91udc_resume(struct platform_device *pdev){ struct at91_udc *udc = platform_get_drvdata(pdev); if (udc->board.vbus_pin > 0 && udc->active_suspend) disable_irq_wake(udc->board.vbus_pin); /* maybe reconnect to host; if so, clocks on */ if (udc->active_suspend) disable_irq_wake(udc->udp_irq); else pullup(udc, 1); return 0;}#else#define at91udc_suspend NULL#define at91udc_resume NULL#endifstatic struct platform_driver at91_udc_driver = { .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, .driver = { .name = (char *) driver_name, .owner = THIS_MODULE, },};static int __init udc_init_module(void){ return platform_driver_probe(&at91_udc_driver, at91udc_probe);}module_init(udc_init_module);static void __exit udc_exit_module(void){ platform_driver_unregister(&at91_udc_driver);}module_exit(udc_exit_module);MODULE_DESCRIPTION("AT91 udc driver");MODULE_AUTHOR("Thomas Rathbone, David Brownell");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -