📄 pxa2xx_udc.c
字号:
handled = 1; } /* endpoint data transfers */ for (i = 0; i < 8; i++) { u32 tmp = 1 << i; if (i && (usir0 & tmp)) { handle_ep(&dev->ep[i]); USIR0 |= tmp; handled = 1; } if (usir1 & tmp) { handle_ep(&dev->ep[i+8]); USIR1 |= tmp; handled = 1; } } } /* we could also ask for 1 msec SOF (SIR) interrupts */ } while (handled); return IRQ_HANDLED;}/*-------------------------------------------------------------------------*/static void nop_release (struct device *dev){ DMSG("%s %s\n", __FUNCTION__, dev->bus_id);}/* this uses load-time allocation and initialization (instead of * doing it at run-time) to save code, eliminate fault paths, and * be more obviously correct. */static struct pxa2xx_udc memory = { .gadget = { .ops = &pxa2xx_udc_ops, .ep0 = &memory.ep[0].ep, .name = driver_name, .dev = { .bus_id = "gadget", .release = nop_release, }, }, /* control endpoint */ .ep[0] = { .ep = { .name = ep0name, .ops = &pxa2xx_ep_ops, .maxpacket = EP0_FIFO_SIZE, }, .dev = &memory, .reg_udccs = &UDCCS0, .reg_uddr = &UDDR0, }, /* first group of endpoints */ .ep[1] = { .ep = { .name = "ep1in-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 1, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS1, .reg_uddr = &UDDR1, }, .ep[2] = { .ep = { .name = "ep2out-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 2, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS2, .reg_ubcr = &UBCR2, .reg_uddr = &UDDR2, },#ifndef CONFIG_USB_PXA2XX_SMALL .ep[3] = { .ep = { .name = "ep3in-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 3, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS3, .reg_uddr = &UDDR3, }, .ep[4] = { .ep = { .name = "ep4out-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 4, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS4, .reg_ubcr = &UBCR4, .reg_uddr = &UDDR4, }, .ep[5] = { .ep = { .name = "ep5in-int", .ops = &pxa2xx_ep_ops, .maxpacket = INT_FIFO_SIZE, }, .dev = &memory, .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 5, .bmAttributes = USB_ENDPOINT_XFER_INT, .reg_udccs = &UDCCS5, .reg_uddr = &UDDR5, }, /* second group of endpoints */ .ep[6] = { .ep = { .name = "ep6in-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 6, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS6, .reg_uddr = &UDDR6, }, .ep[7] = { .ep = { .name = "ep7out-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 7, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS7, .reg_ubcr = &UBCR7, .reg_uddr = &UDDR7, }, .ep[8] = { .ep = { .name = "ep8in-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 8, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS8, .reg_uddr = &UDDR8, }, .ep[9] = { .ep = { .name = "ep9out-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 9, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS9, .reg_ubcr = &UBCR9, .reg_uddr = &UDDR9, }, .ep[10] = { .ep = { .name = "ep10in-int", .ops = &pxa2xx_ep_ops, .maxpacket = INT_FIFO_SIZE, }, .dev = &memory, .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 10, .bmAttributes = USB_ENDPOINT_XFER_INT, .reg_udccs = &UDCCS10, .reg_uddr = &UDDR10, }, /* third group of endpoints */ .ep[11] = { .ep = { .name = "ep11in-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 11, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS11, .reg_uddr = &UDDR11, }, .ep[12] = { .ep = { .name = "ep12out-bulk", .ops = &pxa2xx_ep_ops, .maxpacket = BULK_FIFO_SIZE, }, .dev = &memory, .fifo_size = BULK_FIFO_SIZE, .bEndpointAddress = 12, .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS12, .reg_ubcr = &UBCR12, .reg_uddr = &UDDR12, }, .ep[13] = { .ep = { .name = "ep13in-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 13, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS13, .reg_uddr = &UDDR13, }, .ep[14] = { .ep = { .name = "ep14out-iso", .ops = &pxa2xx_ep_ops, .maxpacket = ISO_FIFO_SIZE, }, .dev = &memory, .fifo_size = ISO_FIFO_SIZE, .bEndpointAddress = 14, .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS14, .reg_ubcr = &UBCR14, .reg_uddr = &UDDR14, }, .ep[15] = { .ep = { .name = "ep15in-int", .ops = &pxa2xx_ep_ops, .maxpacket = INT_FIFO_SIZE, }, .dev = &memory, .fifo_size = INT_FIFO_SIZE, .bEndpointAddress = USB_DIR_IN | 15, .bmAttributes = USB_ENDPOINT_XFER_INT, .reg_udccs = &UDCCS15, .reg_uddr = &UDDR15, },#endif /* !CONFIG_USB_PXA2XX_SMALL */};#define CP15R0_VENDOR_MASK 0xffffe000#if defined(CONFIG_ARCH_PXA)#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */#elif defined(CONFIG_ARCH_IXP4XX)#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */#endif#define CP15R0_PROD_MASK 0x000003f0#define PXA25x 0x00000100 /* and PXA26x */#define PXA210 0x00000120#define CP15R0_REV_MASK 0x0000000f#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK)#define PXA255_A0 0x00000106 /* or PXA260_B1 */#define PXA250_C0 0x00000105 /* or PXA26x_B0 */#define PXA250_B2 0x00000104#define PXA250_B1 0x00000103 /* or PXA260_A0 */#define PXA250_B0 0x00000102#define PXA250_A1 0x00000101#define PXA250_A0 0x00000100#define PXA210_C0 0x00000125#define PXA210_B2 0x00000124#define PXA210_B1 0x00000123#define PXA210_B0 0x00000122#define IXP425_A0 0x000001c1#define IXP425_B0 0x000001f1#define IXP465_AD 0x00000200/* * probe - binds to the platform device */static int __init pxa2xx_udc_probe(struct platform_device *pdev){ struct pxa2xx_udc *dev = &memory; int retval, vbus_irq, irq; u32 chiprev; /* insist on Intel/ARM/XScale */ asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { printk(KERN_ERR "%s: not XScale!\n", driver_name); return -ENODEV; } /* trigger chiprev-specific logic */ switch (chiprev & CP15R0_PRODREV_MASK) {#if defined(CONFIG_ARCH_PXA) case PXA255_A0: dev->has_cfr = 1; break; case PXA250_A0: case PXA250_A1: /* A0/A1 "not released"; ep 13, 15 unusable */ /* fall through */ case PXA250_B2: case PXA210_B2: case PXA250_B1: case PXA210_B1: case PXA250_B0: case PXA210_B0: /* OUT-DMA is broken ... */ /* fall through */ case PXA250_C0: case PXA210_C0: break;#elif defined(CONFIG_ARCH_IXP4XX) case IXP425_A0: case IXP425_B0: case IXP465_AD: dev->has_cfr = 1; break;#endif default: printk(KERN_ERR "%s: unrecognized processor: %08x\n", driver_name, chiprev); /* iop3xx, ixp4xx, ... */ return -ENODEV; } irq = platform_get_irq(pdev, 0); if (irq < 0) return -ENODEV;#ifdef CONFIG_ARCH_PXA dev->clk = clk_get(&pdev->dev, "UDCCLK"); if (IS_ERR(dev->clk)) { retval = PTR_ERR(dev->clk); goto err_clk; }#endif pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", SIZE_STR "(pio)" ); /* other non-static parts of init */ dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; if (dev->mach->gpio_vbus) { if ((retval = gpio_request(dev->mach->gpio_vbus, "pxa2xx_udc GPIO VBUS"))) { dev_dbg(&pdev->dev, "can't get vbus gpio %d, err: %d\n", dev->mach->gpio_vbus, retval); goto err_gpio_vbus; } gpio_direction_input(dev->mach->gpio_vbus); vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); } else vbus_irq = 0; if (dev->mach->gpio_pullup) { if ((retval = gpio_request(dev->mach->gpio_pullup, "pca2xx_udc GPIO PULLUP"))) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); goto err_gpio_pullup; } gpio_direction_output(dev->mach->gpio_pullup, 0); } init_timer(&dev->timer); dev->timer.function = udc_watchdog; dev->timer.data = (unsigned long) dev; device_initialize(&dev->gadget.dev); dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; the_controller = dev; platform_set_drvdata(pdev, dev); udc_disable(dev); udc_reinit(dev); dev->vbus = is_vbus_present(); /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa2xx_udc_irq, IRQF_DISABLED, driver_name, dev); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %d, err %d\n", driver_name, irq, retval); goto err_irq1; } dev->got_irq = 1;#ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, IRQF_DISABLED | IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval);lubbock_fail0: goto err_irq_lub; } retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, IRQF_DISABLED | IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_IRQ, retval); free_irq(LUBBOCK_USB_DISC_IRQ, dev); goto lubbock_fail0; } } else#endif if (vbus_irq) { retval = request_irq(vbus_irq, udc_vbus_irq, IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, driver_name, dev); if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, vbus_irq, retval); goto err_vbus_irq; } } create_proc_files(); return 0; err_vbus_irq:#ifdef CONFIG_ARCH_LUBBOCK free_irq(LUBBOCK_USB_DISC_IRQ, dev); err_irq_lub:#endif free_irq(irq, dev); err_irq1: if (dev->mach->gpio_pullup) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: if (dev->mach->gpio_vbus) gpio_free(dev->mach->gpio_vbus); err_gpio_vbus:#ifdef CONFIG_ARCH_PXA clk_put(dev->clk); err_clk:#endif return retval;}static void pxa2xx_udc_shutdown(struct platform_device *_dev){ pullup_off();}static int __exit pxa2xx_udc_remove(struct platform_device *pdev){ struct pxa2xx_udc *dev = platform_get_drvdata(pdev); if (dev->driver) return -EBUSY; udc_disable(dev); remove_proc_files(); if (dev->got_irq) { free_irq(platform_get_irq(pdev, 0), dev); dev->got_irq = 0; }#ifdef CONFIG_ARCH_LUBBOCK if (machine_is_lubbock()) { free_irq(LUBBOCK_USB_DISC_IRQ, dev); free_irq(LUBBOCK_USB_IRQ, dev); }#endif if (dev->mach->gpio_vbus) { free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); gpio_free(dev->mach->gpio_vbus); } if (dev->mach->gpio_pullup) gpio_free(dev->mach->gpio_pullup);#ifdef CONFIG_ARCH_PXA clk_put(dev->clk);#endif platform_set_drvdata(pdev, NULL); the_controller = NULL; return 0;}/*-------------------------------------------------------------------------*/#ifdef CONFIG_PM/* USB suspend (controlled by the host) and system suspend (controlled * by the PXA) don't necessarily work well together. If USB is active, * the 48 MHz clock is required; so the system can't enter 33 MHz idle * mode, or any deeper PM saving state. * * For now, we punt and forcibly disconnect from the USB host when PXA * enters any suspend state. While we're disconnected, we always disable * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. * Boards without software pullup control shouldn't use those states. * VBUS IRQs should probably be ignored so that the PXA device just acts * "dead" to USB hosts until system resume. */static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state){ struct pxa2xx_udc *udc = platform_get_drvdata(dev); if (!udc->mach->gpio_pullup && !udc->mach->udc_command) WARN("USB host won't detect disconnect!\n"); pullup(udc, 0); return 0;}static int pxa2xx_udc_resume(struct platform_device *dev){ struct pxa2xx_udc *udc = platform_get_drvdata(dev); pullup(udc, 1); return 0;}#else#define pxa2xx_udc_suspend NULL#define pxa2xx_udc_resume NULL#endif/*-------------------------------------------------------------------------*/static struct platform_driver udc_driver = { .shutdown = pxa2xx_udc_shutdown, .remove = __exit_p(pxa2xx_udc_remove), .suspend = pxa2xx_udc_suspend, .resume = pxa2xx_udc_resume, .driver = { .owner = THIS_MODULE, .name = "pxa2xx-udc", },};static int __init udc_init(void){ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);}module_init(udc_init);static void __exit udc_exit(void){ platform_driver_unregister(&udc_driver);}module_exit(udc_exit);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -