⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
			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 + -