Finally, an embedded Linux platform that handles USB wakeup events.  :)
UDC updates for AT91 series processors:

 - Get ready for at91sam926x processors (ARMv5tej not ARMv4t)

 - Suspend/resume support now behaves properly

 - In "standby" mode, UDC can be a source of system wakeup events
   (host resume, device connect/disconnect, etc)

 - Fix a startup problem that crept in with the initial merge

And minor cleanups, especially whitespace.

Signed-off-by: David Brownell <[EMAIL PROTECTED]>
Signed-off-by: Andrew Victor <[EMAIL PROTECTED]>


--- linux-2.6.17/drivers/usb/gadget/at91_udc.h	Mon Jun 19 14:45:12 2006
+++ linux-2.6.17-rc/drivers/usb/gadget/at91_udc.h	Tue May 16 16:45:44 2006
@@ -141,6 +141,7 @@
 	struct clk			*iclk, *fclk;
 	struct platform_device		*pdev;
 	struct proc_dir_entry		*pde;
+	int				udp_irq;
 };
 
 static inline struct at91_udc *to_udc(struct usb_gadget *g)
--- linux-2.6/drivers/usb/gadget/at91_udc.c	2006-04-14 17:25:10.000000000 -0700
+++ at91/drivers/usb/gadget/at91_udc.c	2006-06-19 12:50:05.000000000 -0700
@@ -70,7 +70,7 @@
  * power.  During USB suspend, the 48 MHz clock is gated off.
  */
 
-#define	DRIVER_VERSION	"8 March 2005"
+#define	DRIVER_VERSION	"3 May 2006"
 
 static const char driver_name [] = "at91_udc";
 static const char ep0name[] = "ep0";
@@ -319,7 +319,8 @@ static void done(struct at91_ep *ep, str
  * that shouldn't normally be changed.
  */
 #define	SET_FX	(AT91_UDP_TXPKTRDY)
-#define	CLR_FX	(RX_DATA_READY | AT91_UDP_RXSETUP | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
+#define	CLR_FX	(RX_DATA_READY | AT91_UDP_RXSETUP \
+		| AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
 
 /* pull OUT packet data from the endpoint's fifo */
 static int read_fifo (struct at91_ep *ep, struct at91_request *req)
@@ -473,7 +474,8 @@ static void nuke(struct at91_ep *ep, int
 
 /*-------------------------------------------------------------------------*/
 
-static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+static int at91_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
 {
 	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);
 	struct at91_udc	*dev = ep->udc;
@@ -583,11 +585,12 @@ static int at91_ep_disable (struct usb_e
  * interesting for request or buffer allocation.
  */
 
-static struct usb_request *at91_ep_alloc_request (struct usb_ep *_ep, unsigned int gfp_flags)
+static struct usb_request *
+at91_ep_alloc_request(struct usb_ep *_ep, unsigned int gfp_flags)
 {
 	struct at91_request *req;
 
-	req = kcalloc(1, sizeof (struct at91_request), SLAB_KERNEL);
+	req = kcalloc(1, sizeof (struct at91_request), gfp_flags);
 	if (!req)
 		return NULL;
 
@@ -890,8 +893,8 @@ static void clk_off(struct at91_udc *udc
 		return;
 	udc->clocked = 0;
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
-	clk_disable(udc->iclk);
 	clk_disable(udc->fclk);
+	clk_disable(udc->iclk);
 }
 
 /*
@@ -912,9 +915,6 @@ static void pullup(struct at91_udc *udc,
 		at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
 		at91_set_gpio_value(udc->board.pullup_pin, 0);
 		clk_off(udc);
-
-		// REVISIT:  with transceiver disabled, will D- float
-		// so that a host would falsely detect a device?
 	}
 }
 
@@ -1291,7 +1291,8 @@ static void handle_ep0(struct at91_udc *
 			if (udc->wait_for_addr_ack) {
 				u32	tmp;
 
-				at91_udp_write(AT91_UDP_FADDR, AT91_UDP_FEN | udc->addr);
+				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)
@@ -1362,9 +1363,10 @@ static irqreturn_t at91_udc_irq (int irq
 	u32			rescans = 5;
 
 	while (rescans--) {
-		u32	status = at91_udp_read(AT91_UDP_ISR);
+		u32 status;
 
-		status &= at91_udp_read(AT91_UDP_IMR);
+		status = at91_udp_read(AT91_UDP_ISR)
+			& at91_udp_read(AT91_UDP_IMR);
 		if (!status)
 			break;
 
@@ -1380,18 +1382,17 @@ static irqreturn_t at91_udc_irq (int irq
 			stop_activity(udc);
 
 			/* enable ep0 */
-			at91_udp_write(AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
+			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...
+			 * 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) */
@@ -1453,13 +1454,19 @@ static irqreturn_t at91_udc_irq (int irq
 
 /*-------------------------------------------------------------------------*/
 
+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"
+		.ops	= &at91_udc_ops,
+		.ep0	= &controller.ep[0].ep,
+		.name	= driver_name,
+		.dev	= {
+			.bus_id = "gadget",
+			.release = nop_release,
 		}
 	},
 	.ep[0] = {
@@ -1469,7 +1476,8 @@ static struct at91_udc controller = {
 		},
 		.udc		= &controller,
 		.maxpacket	= 8,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(0)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(0)),
 		.int_mask	= 1 << 0,
 	},
 	.ep[1] = {
@@ -1480,7 +1488,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 64,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(1)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(1)),
 		.int_mask	= 1 << 1,
 	},
 	.ep[2] = {
@@ -1491,7 +1500,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 64,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(2)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(2)),
 		.int_mask	= 1 << 2,
 	},
 	.ep[3] = {
@@ -1502,7 +1512,8 @@ static struct at91_udc controller = {
 		},
 		.udc		= &controller,
 		.maxpacket	= 8,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(3)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(3)),
 		.int_mask	= 1 << 3,
 	},
 	.ep[4] = {
@@ -1513,7 +1524,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 256,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(4)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(4)),
 		.int_mask	= 1 << 4,
 	},
 	.ep[5] = {
@@ -1524,10 +1536,11 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 256,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(5)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(5)),
 		.int_mask	= 1 << 5,
 	},
-	/* ep6 and ep7 are also reserved */
+	/* ep6 and ep7 are also reserved (custom silicon might use them) */
 };
 
 static irqreturn_t at91_vbus_irq(int irq, void *_udc, struct pt_regs *r)
@@ -1625,6 +1638,16 @@ static int __devinit at91udc_probe(struc
 		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;
+	}
+
 	if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) {
 		DBG("someone's using UDC memory\n");
 		return -EBUSY;
@@ -1650,19 +1673,26 @@ static int __devinit at91udc_probe(struc
 	if (retval < 0)
 		goto fail0;
 
-	/* disable everything until there's a gadget driver and vbus */
-	pullup(udc, 0);
+	/* don't do anything until we have both gadget driver and VBUS */
+	clk_enable(udc->iclk);
+	at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+	at91_udp_write(AT91_UDP_IDR, 0xffffffff);
+	clk_disable(udc->iclk);
 
 	/* 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);
+	udc->udp_irq = platform_get_irq(pdev, 0);
+	if (request_irq(udc->udp_irq, at91_udc_irq,
+			SA_INTERRUPT, driver_name, udc)) {
+		DBG("request irq %d failed\n", udc->udp_irq);
 		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);
+		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(udc->udp_irq, udc);
 			retval = -EBUSY;
 			goto fail1;
 		}
@@ -1671,6 +1701,7 @@ static int __devinit at91udc_probe(struc
 		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);
@@ -1679,14 +1710,14 @@ static int __devinit at91udc_probe(struc
 fail1:
 	device_unregister(&udc->gadget.dev);
 fail0:
-	release_mem_region(AT91_VA_BASE_UDP, SZ_16K);
+	release_mem_region(AT91_BASE_UDP, SZ_16K);
 	DBG("%s probe failed, %d\n", driver_name, retval);
 	return retval;
 }
 
-static int __devexit at91udc_remove(struct platform_device *dev)
+static int __devexit at91udc_remove(struct platform_device *pdev)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
 
 	DBG("remove\n");
 
@@ -1695,10 +1726,11 @@ static int __devexit at91udc_remove(stru
 	if (udc->driver != 0)
 		usb_gadget_unregister_driver(udc->driver);
 
+	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(AT91_ID_UDP, udc);
+	free_irq(udc->udp_irq, udc);
 	device_unregister(&udc->gadget.dev);
 	release_mem_region(AT91_BASE_UDP, SZ_16K);
 
@@ -1709,31 +1741,36 @@ static int __devexit at91udc_remove(stru
 }
 
 #ifdef CONFIG_PM
-static int at91udc_suspend(struct platform_device *dev, pm_message_t mesg)
+static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
+	int		wake = udc->driver && device_may_wakeup(&pdev->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.
+	/* 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->vbus && !udc->suspended)
+	if ((!udc->suspended && udc->addr)
+			|| !wake
+			|| at91_suspend_entering_slow_clock()) {
 		pullup(udc, 0);
+		disable_irq_wake(udc->udp_irq);
+	} else
+		enable_irq_wake(udc->udp_irq);
+
+	if (udc->board.vbus_pin > 0) {
+		if (wake)
+			enable_irq_wake(udc->board.vbus_pin);
+		else
+			disable_irq_wake(udc->board.vbus_pin);
+	}
 	return 0;
 }
 
-static int at91udc_resume(struct platform_device *dev)
+static int at91udc_resume(struct platform_device *pdev)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
 
 	/* maybe reconnect to host; if so, clocks on */
 	pullup(udc, 1);
@@ -1749,7 +1786,7 @@ static struct platform_driver at91_udc =
 	.remove		= __devexit_p(at91udc_remove),
 	.shutdown	= at91udc_shutdown,
 	.suspend	= at91udc_suspend,
-	.resume 	= at91udc_resume,
+	.resume		= at91udc_resume,
 	.driver		= {
 		.name	= (char *) driver_name,
 		.owner	= THIS_MODULE,
@@ -1768,6 +1805,6 @@ static void __devexit udc_exit_module(vo
 }
 module_exit(udc_exit_module);
 
-MODULE_DESCRIPTION("AT91RM9200 udc driver");
+MODULE_DESCRIPTION("AT91 udc driver");
 MODULE_AUTHOR("Thomas Rathbone, David Brownell");
 MODULE_LICENSE("GPL");
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to