| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * EHCI HCD (Host Controller Driver) for USB. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Bus Glue for AMD Alchemy Au1xxx | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Modified for AMD Alchemy Au1200 EHC | 
					
						
							|  |  |  |  *  by K.Boge <karsten.boge@amd.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file is licenced under the GPL. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <asm/mach-au1x00/au1000.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define USB_HOST_CONFIG   (USB_MSR_BASE + USB_MSR_MCFG)
 | 
					
						
							|  |  |  | #define USB_MCFG_PFEN     (1<<31)
 | 
					
						
							|  |  |  | #define USB_MCFG_RDCOMB   (1<<30)
 | 
					
						
							|  |  |  | #define USB_MCFG_SSDEN    (1<<23)
 | 
					
						
							|  |  |  | #define USB_MCFG_PHYPLLEN (1<<19)
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | #define USB_MCFG_UCECLKEN (1<<18)
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | #define USB_MCFG_EHCCLKEN (1<<17)
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | #ifdef CONFIG_DMA_COHERENT
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | #define USB_MCFG_UCAM     (1<<7)
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | #else
 | 
					
						
							|  |  |  | #define USB_MCFG_UCAM     (0)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | #define USB_MCFG_EBMEN    (1<<3)
 | 
					
						
							|  |  |  | #define USB_MCFG_EMEMEN   (1<<2)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | #define USBH_ENABLE_CE	(USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
 | 
					
						
							|  |  |  | #define USBH_ENABLE_INIT (USB_MCFG_PFEN  | USB_MCFG_RDCOMB |	\
 | 
					
						
							|  |  |  | 			  USBH_ENABLE_CE | USB_MCFG_SSDEN  |	\ | 
					
						
							|  |  |  | 			  USB_MCFG_UCAM  | USB_MCFG_EBMEN  |	\ | 
					
						
							|  |  |  | 			  USB_MCFG_EMEMEN) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define USBH_DISABLE      (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extern int usb_disabled(void); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | static void au1xxx_start_ehc(void) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	/* enable clock to EHCI block and HS PHY PLL*/ | 
					
						
							|  |  |  | 	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG); | 
					
						
							|  |  |  | 	au_sync(); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	udelay(1000); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	/* enable EHCI mmio */ | 
					
						
							|  |  |  | 	au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG); | 
					
						
							|  |  |  | 	au_sync(); | 
					
						
							|  |  |  | 	udelay(1000); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | static void au1xxx_stop_ehc(void) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	unsigned long c; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable mem */ | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG); | 
					
						
							|  |  |  | 	au_sync(); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	udelay(1000); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable EHC clock. If the HS PHY is unused disable it too. */ | 
					
						
							|  |  |  | 	c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN; | 
					
						
							|  |  |  | 	if (!(c & USB_MCFG_UCECLKEN))		/* UDC disabled? */ | 
					
						
							|  |  |  | 		c &= ~USB_MCFG_PHYPLLEN;	/* yes: disable HS PHY PLL */ | 
					
						
							|  |  |  | 	au_writel(c, USB_HOST_CONFIG); | 
					
						
							|  |  |  | 	au_sync(); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-08 20:43:32 +01:00
										 |  |  | static int au1xxx_ehci_setup(struct usb_hcd *hcd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 
					
						
							|  |  |  | 	int ret = ehci_init(hcd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ehci->need_io_watchdog = 0; | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | static const struct hc_driver ehci_au1xxx_hc_driver = { | 
					
						
							|  |  |  | 	.description		= hcd_name, | 
					
						
							|  |  |  | 	.product_desc		= "Au1xxx EHCI", | 
					
						
							|  |  |  | 	.hcd_priv_size		= sizeof(struct ehci_hcd), | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * generic hardware linkage | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.irq			= ehci_irq, | 
					
						
							|  |  |  | 	.flags			= HCD_MEMORY | HCD_USB2, | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * basic lifecycle operations | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * FIXME -- ehci_init() doesn't do enough here. | 
					
						
							|  |  |  | 	 * See ehci-ppc-soc for a complete implementation. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2010-03-08 20:43:32 +01:00
										 |  |  | 	.reset			= au1xxx_ehci_setup, | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	.start			= ehci_run, | 
					
						
							|  |  |  | 	.stop			= ehci_stop, | 
					
						
							|  |  |  | 	.shutdown		= ehci_shutdown, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * managing i/o requests and associated device resources | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.urb_enqueue		= ehci_urb_enqueue, | 
					
						
							|  |  |  | 	.urb_dequeue		= ehci_urb_dequeue, | 
					
						
							|  |  |  | 	.endpoint_disable	= ehci_endpoint_disable, | 
					
						
							| 
									
										
										
										
											2009-05-27 18:21:56 -04:00
										 |  |  | 	.endpoint_reset		= ehci_endpoint_reset, | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * scheduling support | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.get_frame_number	= ehci_get_frame, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * root hub support | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.hub_status_data	= ehci_hub_status_data, | 
					
						
							|  |  |  | 	.hub_control		= ehci_hub_control, | 
					
						
							|  |  |  | 	.bus_suspend		= ehci_bus_suspend, | 
					
						
							|  |  |  | 	.bus_resume		= ehci_bus_resume, | 
					
						
							|  |  |  | 	.relinquish_port	= ehci_relinquish_port, | 
					
						
							|  |  |  | 	.port_handed_over	= ehci_port_handed_over, | 
					
						
							| 
									
										
										
										
											2009-06-29 10:47:30 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete, | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct usb_hcd *hcd; | 
					
						
							|  |  |  | 	struct ehci_hcd *ehci; | 
					
						
							| 
									
										
										
										
											2009-12-14 18:22:42 -05:00
										 |  |  | 	struct resource *res; | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	if (usb_disabled()) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | #if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	/* Au1200 AB USB does not support coherent memory */ | 
					
						
							|  |  |  | 	if (!(read_c0_prid() & 0xff)) { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 		printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name); | 
					
						
							|  |  |  | 		printk(KERN_INFO "%s: update your board or re-configure" | 
					
						
							|  |  |  | 				 " the kernel\n", pdev->name); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	if (pdev->resource[1].flags != IORESOURCE_IRQ) { | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 		pr_debug("resource[1] is not IORESOURCE_IRQ"); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx"); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	if (!hcd) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 18:22:42 -05:00
										 |  |  | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
					
						
							|  |  |  | 	hcd->rsrc_start = res->start; | 
					
						
							|  |  |  | 	hcd->rsrc_len = resource_size(res); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { | 
					
						
							|  |  |  | 		pr_debug("request_mem_region failed"); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 		ret = -EBUSY; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 		goto err1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | 
					
						
							|  |  |  | 	if (!hcd->regs) { | 
					
						
							|  |  |  | 		pr_debug("ioremap failed"); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 		ret = -ENOMEM; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 		goto err2; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	au1xxx_start_ehc(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	ehci = hcd_to_ehci(hcd); | 
					
						
							|  |  |  | 	ehci->caps = hcd->regs; | 
					
						
							|  |  |  | 	ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); | 
					
						
							|  |  |  | 	/* cache this readonly data; minimize chip reads */ | 
					
						
							|  |  |  | 	ehci->hcs_params = readl(&ehci->caps->hcs_params); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	ret = usb_add_hcd(hcd, pdev->resource[1].start, | 
					
						
							|  |  |  | 			  IRQF_DISABLED | IRQF_SHARED); | 
					
						
							|  |  |  | 	if (ret == 0) { | 
					
						
							|  |  |  | 		platform_set_drvdata(pdev, hcd); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	au1xxx_stop_ehc(); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	iounmap(hcd->regs); | 
					
						
							|  |  |  | err2: | 
					
						
							|  |  |  | 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | 
					
						
							|  |  |  | err1: | 
					
						
							|  |  |  | 	usb_put_hcd(hcd); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	struct usb_hcd *hcd = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 	usb_remove_hcd(hcd); | 
					
						
							|  |  |  | 	iounmap(hcd->regs); | 
					
						
							|  |  |  | 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | 
					
						
							|  |  |  | 	usb_put_hcd(hcd); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	au1xxx_stop_ehc(); | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, NULL); | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | #ifdef CONFIG_PM
 | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | 	struct usb_hcd *hcd = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 	struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 	rc = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (time_before(jiffies, ehci->next_statechange)) | 
					
						
							|  |  |  | 		msleep(10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Root hub was already suspended. Disable irq emission and
 | 
					
						
							| 
									
										
										
										
											2010-05-12 18:21:35 -04:00
										 |  |  | 	 * mark HW unaccessible.  The PM and USB cores make sure that | 
					
						
							|  |  |  | 	 * the root hub is either suspended or stopped. | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	spin_lock_irqsave(&ehci->lock, flags); | 
					
						
							| 
									
										
										
										
											2010-06-25 14:02:14 -04:00
										 |  |  | 	ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 	ehci_writel(ehci, 0, &ehci->regs->intr_enable); | 
					
						
							|  |  |  | 	(void)ehci_readl(ehci, &ehci->regs->intr_enable); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	au1xxx_stop_ehc(); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&ehci->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// could save FLADJ in case of Vaux power loss
 | 
					
						
							|  |  |  | 	// ... we'd only use it to handle clock skew
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rc; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | static int ehci_hcd_au1xxx_drv_resume(struct device *dev) | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | 	struct usb_hcd *hcd = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 	struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	au1xxx_start_ehc(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// maybe restore FLADJ
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (time_before(jiffies, ehci->next_statechange)) | 
					
						
							|  |  |  | 		msleep(100); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Mark hardware accessible again as we are out of D3 state by now */ | 
					
						
							|  |  |  | 	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If CF is still set, we maintained PCI Vaux power.
 | 
					
						
							|  |  |  | 	 * Just undo the effect of ehci_pci_suspend(). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { | 
					
						
							|  |  |  | 		int	mask = INTR_MASK; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-12 18:21:35 -04:00
										 |  |  | 		ehci_prepare_ports_for_controller_resume(ehci); | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 		if (!hcd->self.root_hub->do_remote_wakeup) | 
					
						
							|  |  |  | 			mask &= ~STS_PCD; | 
					
						
							|  |  |  | 		ehci_writel(ehci, mask, &ehci->regs->intr_enable); | 
					
						
							|  |  |  | 		ehci_readl(ehci, &ehci->regs->intr_enable); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ehci_dbg(ehci, "lost power, restarting\n"); | 
					
						
							|  |  |  | 	usb_root_hub_lost_power(hcd->self.root_hub); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Else reset, to cope with power loss or flush-to-storage
 | 
					
						
							|  |  |  | 	 * style "resume" having let BIOS kick in during reboot. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	(void) ehci_halt(ehci); | 
					
						
							|  |  |  | 	(void) ehci_reset(ehci); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* emptying the schedule aborts any urbs */ | 
					
						
							|  |  |  | 	spin_lock_irq(&ehci->lock); | 
					
						
							|  |  |  | 	if (ehci->reclaim) | 
					
						
							|  |  |  | 		end_unlink_async(ehci); | 
					
						
							|  |  |  | 	ehci_work(ehci); | 
					
						
							|  |  |  | 	spin_unlock_irq(&ehci->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ehci_writel(ehci, ehci->command, &ehci->regs->command); | 
					
						
							|  |  |  | 	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); | 
					
						
							|  |  |  | 	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* here we "know" root ports should always stay powered */ | 
					
						
							|  |  |  | 	ehci_port_power(ehci, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hcd->state = HC_STATE_SUSPENDED; | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 18:00:08 -08:00
										 |  |  | static const struct dev_pm_ops au1xxx_ehci_pmops = { | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | 	.suspend	= ehci_hcd_au1xxx_drv_suspend, | 
					
						
							|  |  |  | 	.resume		= ehci_hcd_au1xxx_drv_resume, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define AU1XXX_EHCI_PMOPS &au1xxx_ehci_pmops
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | #define AU1XXX_EHCI_PMOPS NULL
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:09:37 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-23 21:36:07 +01:00
										 |  |  | static struct platform_driver ehci_hcd_au1xxx_driver = { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 	.probe		= ehci_hcd_au1xxx_drv_probe, | 
					
						
							|  |  |  | 	.remove		= ehci_hcd_au1xxx_drv_remove, | 
					
						
							|  |  |  | 	.shutdown	= usb_hcd_platform_shutdown, | 
					
						
							| 
									
										
										
										
											2006-06-23 21:36:07 +01:00
										 |  |  | 	.driver = { | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 		.name	= "au1xxx-ehci", | 
					
						
							|  |  |  | 		.owner	= THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2009-07-29 19:13:13 +02:00
										 |  |  | 		.pm	= AU1XXX_EHCI_PMOPS, | 
					
						
							| 
									
										
										
										
											2006-06-23 21:36:07 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-01-20 14:06:09 -08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2008-06-23 09:08:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_ALIAS("platform:au1xxx-ehci"); |