1/* 2 * EHCI HCD (Host Controller Driver) for USB. 3 * 4 * Bus Glue for AMD Alchemy Au1xxx 5 * 6 * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org> 7 * 8 * Modified for AMD Alchemy Au1200 EHC 9 * by K.Boge <karsten.boge@amd.com> 10 * 11 * This file is licenced under the GPL. 12 */ 13 14#include <linux/platform_device.h> 15#include <asm/mach-au1x00/au1000.h> 16 17 18extern int usb_disabled(void); 19 20static int au1xxx_ehci_setup(struct usb_hcd *hcd) 21{ 22 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 23 int ret = ehci_init(hcd); 24 25 ehci->need_io_watchdog = 0; 26 ehci_reset(ehci); 27 return ret; 28} 29 30static const struct hc_driver ehci_au1xxx_hc_driver = { 31 .description = hcd_name, 32 .product_desc = "Au1xxx EHCI", 33 .hcd_priv_size = sizeof(struct ehci_hcd), 34 35 /* 36 * generic hardware linkage 37 */ 38 .irq = ehci_irq, 39 .flags = HCD_MEMORY | HCD_USB2, 40 41 /* 42 * basic lifecycle operations 43 * 44 * FIXME -- ehci_init() doesn't do enough here. 45 * See ehci-ppc-soc for a complete implementation. 46 */ 47 .reset = au1xxx_ehci_setup, 48 .start = ehci_run, 49 .stop = ehci_stop, 50 .shutdown = ehci_shutdown, 51 52 /* 53 * managing i/o requests and associated device resources 54 */ 55 .urb_enqueue = ehci_urb_enqueue, 56 .urb_dequeue = ehci_urb_dequeue, 57 .endpoint_disable = ehci_endpoint_disable, 58 .endpoint_reset = ehci_endpoint_reset, 59 60 /* 61 * scheduling support 62 */ 63 .get_frame_number = ehci_get_frame, 64 65 /* 66 * root hub support 67 */ 68 .hub_status_data = ehci_hub_status_data, 69 .hub_control = ehci_hub_control, 70 .bus_suspend = ehci_bus_suspend, 71 .bus_resume = ehci_bus_resume, 72 .relinquish_port = ehci_relinquish_port, 73 .port_handed_over = ehci_port_handed_over, 74 75 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 76}; 77 78static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) 79{ 80 struct usb_hcd *hcd; 81 struct ehci_hcd *ehci; 82 struct resource *res; 83 int ret; 84 85 if (usb_disabled()) 86 return -ENODEV; 87 88 if (pdev->resource[1].flags != IORESOURCE_IRQ) { 89 pr_debug("resource[1] is not IORESOURCE_IRQ"); 90 return -ENOMEM; 91 } 92 hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx"); 93 if (!hcd) 94 return -ENOMEM; 95 96 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 97 hcd->rsrc_start = res->start; 98 hcd->rsrc_len = resource_size(res); 99 100 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 101 pr_debug("request_mem_region failed"); 102 ret = -EBUSY; 103 goto err1; 104 } 105 106 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 107 if (!hcd->regs) { 108 pr_debug("ioremap failed"); 109 ret = -ENOMEM; 110 goto err2; 111 } 112 113 if (alchemy_usb_control(ALCHEMY_USB_EHCI0, 1)) { 114 printk(KERN_INFO "%s: controller init failed!\n", pdev->name); 115 ret = -ENODEV; 116 goto err3; 117 } 118 119 ehci = hcd_to_ehci(hcd); 120 ehci->caps = hcd->regs; 121 ehci->regs = hcd->regs + 122 HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); 123 /* cache this readonly data; minimize chip reads */ 124 ehci->hcs_params = readl(&ehci->caps->hcs_params); 125 126 ret = usb_add_hcd(hcd, pdev->resource[1].start, 127 IRQF_SHARED); 128 if (ret == 0) { 129 platform_set_drvdata(pdev, hcd); 130 return ret; 131 } 132 133 alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); 134err3: 135 iounmap(hcd->regs); 136err2: 137 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 138err1: 139 usb_put_hcd(hcd); 140 return ret; 141} 142 143static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) 144{ 145 struct usb_hcd *hcd = platform_get_drvdata(pdev); 146 147 usb_remove_hcd(hcd); 148 alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); 149 iounmap(hcd->regs); 150 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 151 usb_put_hcd(hcd); 152 platform_set_drvdata(pdev, NULL); 153 154 return 0; 155} 156 157#ifdef CONFIG_PM 158static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) 159{ 160 struct usb_hcd *hcd = dev_get_drvdata(dev); 161 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 162 unsigned long flags; 163 int rc = 0; 164 165 if (time_before(jiffies, ehci->next_statechange)) 166 msleep(10); 167 168 /* Root hub was already suspended. Disable irq emission and 169 * mark HW unaccessible. The PM and USB cores make sure that 170 * the root hub is either suspended or stopped. 171 */ 172 ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); 173 spin_lock_irqsave(&ehci->lock, flags); 174 ehci_writel(ehci, 0, &ehci->regs->intr_enable); 175 (void)ehci_readl(ehci, &ehci->regs->intr_enable); 176 177 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 178 spin_unlock_irqrestore(&ehci->lock, flags); 179 180 // could save FLADJ in case of Vaux power loss 181 // ... we'd only use it to handle clock skew 182 183 alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); 184 185 return rc; 186} 187 188static int ehci_hcd_au1xxx_drv_resume(struct device *dev) 189{ 190 struct usb_hcd *hcd = dev_get_drvdata(dev); 191 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 192 193 alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); 194 195 // maybe restore FLADJ 196 197 if (time_before(jiffies, ehci->next_statechange)) 198 msleep(100); 199 200 /* Mark hardware accessible again as we are out of D3 state by now */ 201 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 202 203 /* If CF is still set, we maintained PCI Vaux power. 204 * Just undo the effect of ehci_pci_suspend(). 205 */ 206 if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { 207 int mask = INTR_MASK; 208 209 ehci_prepare_ports_for_controller_resume(ehci); 210 if (!hcd->self.root_hub->do_remote_wakeup) 211 mask &= ~STS_PCD; 212 ehci_writel(ehci, mask, &ehci->regs->intr_enable); 213 ehci_readl(ehci, &ehci->regs->intr_enable); 214 return 0; 215 } 216 217 ehci_dbg(ehci, "lost power, restarting\n"); 218 usb_root_hub_lost_power(hcd->self.root_hub); 219 220 /* Else reset, to cope with power loss or flush-to-storage 221 * style "resume" having let BIOS kick in during reboot. 222 */ 223 (void) ehci_halt(ehci); 224 (void) ehci_reset(ehci); 225 226 /* emptying the schedule aborts any urbs */ 227 spin_lock_irq(&ehci->lock); 228 if (ehci->reclaim) 229 end_unlink_async(ehci); 230 ehci_work(ehci); 231 spin_unlock_irq(&ehci->lock); 232 233 ehci_writel(ehci, ehci->command, &ehci->regs->command); 234 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 235 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 236 237 /* here we "know" root ports should always stay powered */ 238 ehci_port_power(ehci, 1); 239 240 ehci->rh_state = EHCI_RH_SUSPENDED; 241 242 return 0; 243} 244 245static const struct dev_pm_ops au1xxx_ehci_pmops = { 246 .suspend = ehci_hcd_au1xxx_drv_suspend, 247 .resume = ehci_hcd_au1xxx_drv_resume, 248}; 249 250#define AU1XXX_EHCI_PMOPS &au1xxx_ehci_pmops 251 252#else 253#define AU1XXX_EHCI_PMOPS NULL 254#endif 255 256static struct platform_driver ehci_hcd_au1xxx_driver = { 257 .probe = ehci_hcd_au1xxx_drv_probe, 258 .remove = ehci_hcd_au1xxx_drv_remove, 259 .shutdown = usb_hcd_platform_shutdown, 260 .driver = { 261 .name = "au1xxx-ehci", 262 .owner = THIS_MODULE, 263 .pm = AU1XXX_EHCI_PMOPS, 264 } 265}; 266 267MODULE_ALIAS("platform:au1xxx-ehci"); 268