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

📄 at91_udc.c

📁 usb driver for 2.6.17
💻 C
📖 第 1 页 / 共 3 页
字号:
			| USB_REQ_CLEAR_FEATURE:		tmp = w_index & USB_ENDPOINT_NUMBER_MASK;		ep = &udc->ep[tmp];		if (w_value != USB_ENDPOINT_HALT || tmp > NUM_ENDPOINTS)			goto stall;		if (tmp == 0)			goto succeed;		if (!ep->desc || ep->is_iso)			goto stall;		if ((w_index & USB_DIR_IN)) {			if (!ep->is_in)				goto stall;		} else if (ep->is_in)			goto stall;		at91_udp_write(AT91_UDP_RST_EP, ep->int_mask);		at91_udp_write(AT91_UDP_RST_EP, 0);		tmp = __raw_readl(ep->creg);		tmp |= CLR_FX;		tmp &= ~(SET_FX | AT91_UDP_FORCESTALL);		__raw_writel(tmp, ep->creg);		if (!list_empty(&ep->queue))			handle_ep(ep);		goto succeed;	}#undef w_value#undef w_index#undef w_length	/* pass request up to the gadget driver */	status = udc->driver->setup(&udc->gadget, &pkt.r);	if (status < 0) {stall:		VDBG("req %02x.%02x protocol STALL; stat %d\n",				pkt.r.bRequestType, pkt.r.bRequest, status);		csr |= AT91_UDP_FORCESTALL;		__raw_writel(csr, creg);		udc->req_pending = 0;	}	return;succeed:	/* immediate successful (IN) STATUS after zero length DATA */	PACKET("ep0 in/status\n");write_in:	csr |= AT91_UDP_TXPKTRDY;	__raw_writel(csr, creg);	udc->req_pending = 0;	return;}static void handle_ep0(struct at91_udc *udc){	struct at91_ep		*ep0 = &udc->ep[0];	u32 __iomem		*creg = ep0->creg;	u32			csr = __raw_readl(creg);	struct at91_request	*req;	if (unlikely(csr & AT91_UDP_STALLSENT)) {		nuke(ep0, -EPROTO);		udc->req_pending = 0;		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_STALLSENT | AT91_UDP_FORCESTALL);		__raw_writel(csr, creg);		VDBG("ep0 stalled\n");		csr = __raw_readl(creg);	}	if (csr & AT91_UDP_RXSETUP) {		nuke(ep0, 0);		udc->req_pending = 0;		handle_setup(udc, ep0, csr);		return;	}	if (list_empty(&ep0->queue))		req = NULL;	else		req = list_entry(ep0->queue.next, struct at91_request, queue);	/* host ACKed an IN packet that we sent */	if (csr & AT91_UDP_TXCOMP) {		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_TXCOMP);		/* write more IN DATA? */		if (req && ep0->is_in) {			if (handle_ep(ep0))				udc->req_pending = 0;		/*		 * Ack after:		 *  - last IN DATA packet (including GET_STATUS)		 *  - IN/STATUS for OUT DATA		 *  - IN/STATUS for any zero-length DATA stage		 * except for the IN DATA case, the host should send		 * an OUT status later, which we'll ack.		 */		} else {			udc->req_pending = 0;			__raw_writel(csr, creg);			/*			 * SET_ADDRESS takes effect only after the STATUS			 * (to the original address) gets acked.			 */			if (udc->wait_for_addr_ack) {				u32	tmp;				at91_udp_write(AT91_UDP_FADDR, AT91_UDP_FEN | udc->addr);				tmp = at91_udp_read(AT91_UDP_GLB_STAT);				tmp &= ~AT91_UDP_FADDEN;				if (udc->addr)					tmp |= AT91_UDP_FADDEN;				at91_udp_write(AT91_UDP_GLB_STAT, tmp);				udc->wait_for_addr_ack = 0;				VDBG("address %d\n", udc->addr);			}		}	}	/* OUT packet arrived ... */	else if (csr & AT91_UDP_RX_DATA_BK0) {		csr |= CLR_FX;		csr &= ~(SET_FX | AT91_UDP_RX_DATA_BK0);		/* OUT DATA stage */		if (!ep0->is_in) {			if (req) {				if (handle_ep(ep0)) {					/* send IN/STATUS */					PACKET("ep0 in/status\n");					csr = __raw_readl(creg);					csr &= ~SET_FX;					csr |= CLR_FX | AT91_UDP_TXPKTRDY;					__raw_writel(csr, creg);					udc->req_pending = 0;				}			} else if (udc->req_pending) {				/*				 * AT91 hardware has a hard time with this				 * "deferred response" mode for control-OUT				 * transfers.  (For control-IN it's fine.)				 *				 * The normal solution leaves OUT data in the				 * fifo until the gadget driver is ready.				 * We couldn't do that here without disabling				 * the IRQ that tells about SETUP packets,				 * e.g. when the host gets impatient...				 *				 * Working around it by copying into a buffer				 * would almost be a non-deferred response,				 * except that it wouldn't permit reliable				 * stalling of the request.  Instead, demand				 * that gadget drivers not use this mode.				 */				DBG("no control-OUT deferred responses!\n");				__raw_writel(csr | AT91_UDP_FORCESTALL, creg);				udc->req_pending = 0;			}		/* STATUS stage for control-IN; ack.  */		} else {			PACKET("ep0 out/status ACK\n");			__raw_writel(csr, creg);			/* "early" status stage */			if (req)				done(ep0, req, 0);		}	}}static irqreturn_t at91_udc_irq (int irq, void *_udc, struct pt_regs *r){	struct at91_udc		*udc = _udc;	u32			rescans = 5;	while (rescans--) {		u32	status = at91_udp_read(AT91_UDP_ISR);		status &= at91_udp_read(AT91_UDP_IMR);		if (!status)			break;		/* USB reset irq:  not maskable */		if (status & AT91_UDP_ENDBUSRES) {			at91_udp_write(AT91_UDP_IDR, ~MINIMUS_INTERRUPTUS);			at91_udp_write(AT91_UDP_IER, MINIMUS_INTERRUPTUS);			/* Atmel code clears this irq twice */			at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);			at91_udp_write(AT91_UDP_ICR, AT91_UDP_ENDBUSRES);			VDBG("end bus reset\n");			udc->addr = 0;			stop_activity(udc);			/* enable ep0 */			at91_udp_write(AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);			udc->gadget.speed = USB_SPEED_FULL;			udc->suspended = 0;			at91_udp_write(AT91_UDP_IER, AT91_UDP_EP(0));			/*			 * NOTE:  this driver keeps clocks off unless the			 * USB host is present.  That saves power, and also			 * eliminates IRQs (reset, resume, suspend) that can			 * otherwise flood from the controller.  If your			 * board doesn't support VBUS detection, suspend and			 * resume irq logic may need more attention...			 */		/* host initiated suspend (3+ms bus idle) */		} else if (status & AT91_UDP_RXSUSP) {			at91_udp_write(AT91_UDP_IDR, AT91_UDP_RXSUSP);			at91_udp_write(AT91_UDP_IER, AT91_UDP_RXRSM);			at91_udp_write(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(AT91_UDP_IDR, AT91_UDP_RXRSM);			at91_udp_write(AT91_UDP_IER, AT91_UDP_RXSUSP);			at91_udp_write(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 struct at91_udc controller = {	.gadget = {		.ops = &at91_udc_ops,		.ep0 = &controller.ep[0].ep,		.name = driver_name,		.dev = {			.bus_id = "gadget"		}	},	.ep[0] = {		.ep = {			.name	= ep0name,			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.maxpacket	= 8,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(0)),		.int_mask	= 1 << 0,	},	.ep[1] = {		.ep = {			.name	= "ep1",			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.is_pingpong	= 1,		.maxpacket	= 64,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(1)),		.int_mask	= 1 << 1,	},	.ep[2] = {		.ep = {			.name	= "ep2",			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.is_pingpong	= 1,		.maxpacket	= 64,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(2)),		.int_mask	= 1 << 2,	},	.ep[3] = {		.ep = {			/* could actually do bulk too */			.name	= "ep3-int",			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.maxpacket	= 8,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(3)),		.int_mask	= 1 << 3,	},	.ep[4] = {		.ep = {			.name	= "ep4",			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.is_pingpong	= 1,		.maxpacket	= 256,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(4)),		.int_mask	= 1 << 4,	},	.ep[5] = {		.ep = {			.name	= "ep5",			.ops	= &at91_ep_ops,		},		.udc		= &controller,		.is_pingpong	= 1,		.maxpacket	= 256,		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(5)),		.int_mask	= 1 << 5,	},	/* ep6 and ep7 are also reserved */};static irqreturn_t at91_vbus_irq(int irq, void *_udc, struct pt_regs *r){	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->unbind			|| !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;		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)		return -EINVAL;	local_irq_disable();	udc->enabled = 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 __devinit at91udc_probe(struct platform_device *pdev){	struct device	*dev = &pdev->dev;	struct at91_udc	*udc;	int		retval;	if (!dev->platform_data) {		/* small (so we copy it) but critical! */		DBG("missing platform_data\n");		return -ENODEV;	}	if (!request_mem_region(AT91_BASE_UDP, SZ_16K, 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_reinit(udc);	udc->enabled = 0;	/* 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");		return -ENODEV;	}	retval = device_register(&udc->gadget.dev);	if (retval < 0)		goto fail0;	/* disable everything until there's a gadget driver and vbus */	pullup(udc, 0);	/* request UDC and maybe VBUS irqs */	if (request_irq(AT91_ID_UDP, at91_udc_irq, SA_INTERRUPT, driver_name, udc)) {		DBG("request irq %d failed\n", AT91_ID_UDP);		retval = -EBUSY;		goto fail1;	}	if (udc->board.vbus_pin > 0) {		if (request_irq(udc->board.vbus_pin, at91_vbus_irq, SA_INTERRUPT, driver_name, udc)) {			DBG("request vbus irq %d failed\n", udc->board.vbus_pin);			free_irq(AT91_ID_UDP, udc);			retval = -EBUSY;			goto fail1;		}	} else {		DBG("no VBUS detection, assuming always-on\n");		udc->vbus = 1;	}	dev_set_drvdata(dev, udc);	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(AT91_VA_BASE_UDP, SZ_16K);	DBG("%s probe failed, %d\n", driver_name, retval);	return retval;}static int __devexit at91udc_remove(struct platform_device *dev){	struct at91_udc *udc = platform_get_drvdata(dev);	DBG("remove\n");	pullup(udc, 0);	if (udc->driver != 0)		usb_gadget_unregister_driver(udc->driver);	remove_debug_file(udc);	if (udc->board.vbus_pin > 0)		free_irq(udc->board.vbus_pin, udc);	free_irq(AT91_ID_UDP, udc);	device_unregister(&udc->gadget.dev);	release_mem_region(AT91_BASE_UDP, SZ_16K);	clk_put(udc->iclk);	clk_put(udc->fclk);	return 0;}#ifdef CONFIG_PMstatic int at91udc_suspend(struct platform_device *dev, pm_message_t mesg){	struct at91_udc *udc = platform_get_drvdata(dev);	/*	 * The "safe" suspend transitions are opportunistic ... e.g. when	 * the USB link is suspended (48MHz clock autogated off), or when	 * it's disconnected (programmatically gated off, elsewhere).	 * Then we can suspend, and the chip can enter slow clock mode.	 *	 * The problem case is some component (user mode?) suspending this	 * device while it's active, with the 48 MHz clock in use.  There	 * are two basic approaches:  (a) veto suspend levels involving slow	 * clock mode, (b) disconnect, so 48 MHz will no longer be in use	 * and we can enter slow clock mode.  This uses (b) for now, since	 * it's simplest until AT91 PM exists and supports the other option.	 */	if (udc->vbus && !udc->suspended)		pullup(udc, 0);	return 0;}static int at91udc_resume(struct platform_device *dev){	struct at91_udc *udc = platform_get_drvdata(dev);	/* maybe reconnect to host; if so, clocks on */	pullup(udc, 1);	return 0;}#else#define	at91udc_suspend	NULL#define	at91udc_resume	NULL#endifstatic struct platform_driver at91_udc = {	.probe		= at91udc_probe,	.remove		= __devexit_p(at91udc_remove),	.shutdown	= at91udc_shutdown,	.suspend	= at91udc_suspend,	.resume 	= at91udc_resume,	.driver		= {		.name	= (char *) driver_name,		.owner	= THIS_MODULE,	},};static int __devinit udc_init_module(void){	return platform_driver_register(&at91_udc);}module_init(udc_init_module);static void __devexit udc_exit_module(void){	platform_driver_unregister(&at91_udc);}module_exit(udc_exit_module);MODULE_DESCRIPTION("AT91RM9200 udc driver");MODULE_AUTHOR("Thomas Rathbone, David Brownell");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -