Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: host: mips: sead3: USB Host controller support for SEAD-3 platform.

Add EHCI driver for MIPS SEAD-3 development platform.

Signed-off-by: Chris Dearman <chris@mips.com>
Signed-off-by: Steven J. Hill <sjhill@mips.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Steven J. Hill and committed by
Greg Kroah-Hartman
c256667f 1f6155f5

+274 -2
+3 -2
drivers/usb/host/Kconfig
··· 110 110 depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ 111 111 ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ 112 112 PPC_MPC512x || CPU_CAVIUM_OCTEON || \ 113 - PMC_MSP || SPARC_LEON) 113 + PMC_MSP || SPARC_LEON || MIPS_SEAD3) 114 114 default y 115 115 116 116 config USB_EHCI_BIG_ENDIAN_DESC 117 117 bool 118 118 depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ 119 - PPC_MPC512x || PMC_MSP || SPARC_LEON) 119 + PPC_MPC512x || PMC_MSP || SPARC_LEON || \ 120 + MIPS_SEAD3) 120 121 default y 121 122 122 123 config XPS_USB_HCD_XILINX
+5
drivers/usb/host/ehci-hcd.c
··· 1364 1364 #define PLATFORM_DRIVER ehci_ls1x_driver 1365 1365 #endif 1366 1366 1367 + #ifdef CONFIG_MIPS_SEAD3 1368 + #include "ehci-sead3.c" 1369 + #define PLATFORM_DRIVER ehci_hcd_sead3_driver 1370 + #endif 1371 + 1367 1372 #ifdef CONFIG_USB_EHCI_HCD_PLATFORM 1368 1373 #include "ehci-platform.c" 1369 1374 #define PLATFORM_DRIVER ehci_platform_driver
+266
drivers/usb/host/ehci-sead3.c
··· 1 + /* 2 + * MIPS CI13320A EHCI Host Controller driver 3 + * Based on "ehci-au1xxx.c" by K.Boge <karsten.boge@amd.com> 4 + * 5 + * Copyright (C) 2012 MIPS Technologies, Inc. 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 + * for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software Foundation, 19 + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 + */ 21 + 22 + #include <linux/platform_device.h> 23 + 24 + static int ehci_sead3_setup(struct usb_hcd *hcd) 25 + { 26 + int ret; 27 + struct ehci_hcd *ehci = hcd_to_ehci(hcd); 28 + 29 + ehci->caps = hcd->regs + 0x100; 30 + 31 + ret = ehci_setup(hcd); 32 + if (ret) 33 + return ret; 34 + 35 + ehci->need_io_watchdog = 0; 36 + 37 + #ifdef __BIG_ENDIAN 38 + ehci->big_endian_mmio = 1; 39 + ehci->big_endian_desc = 1; 40 + #endif 41 + 42 + /* Set burst length to 16 words. */ 43 + ehci_writel(ehci, 0x1010, &ehci->regs->reserved[1]); 44 + 45 + return ret; 46 + } 47 + 48 + const struct hc_driver ehci_sead3_hc_driver = { 49 + .description = hcd_name, 50 + .product_desc = "SEAD-3 EHCI", 51 + .hcd_priv_size = sizeof(struct ehci_hcd), 52 + 53 + /* 54 + * generic hardware linkage 55 + */ 56 + .irq = ehci_irq, 57 + .flags = HCD_MEMORY | HCD_USB2, 58 + 59 + /* 60 + * basic lifecycle operations 61 + * 62 + */ 63 + .reset = ehci_sead3_setup, 64 + .start = ehci_run, 65 + .stop = ehci_stop, 66 + .shutdown = ehci_shutdown, 67 + 68 + /* 69 + * managing i/o requests and associated device resources 70 + */ 71 + .urb_enqueue = ehci_urb_enqueue, 72 + .urb_dequeue = ehci_urb_dequeue, 73 + .endpoint_disable = ehci_endpoint_disable, 74 + .endpoint_reset = ehci_endpoint_reset, 75 + 76 + /* 77 + * scheduling support 78 + */ 79 + .get_frame_number = ehci_get_frame, 80 + 81 + /* 82 + * root hub support 83 + */ 84 + .hub_status_data = ehci_hub_status_data, 85 + .hub_control = ehci_hub_control, 86 + .bus_suspend = ehci_bus_suspend, 87 + .bus_resume = ehci_bus_resume, 88 + .relinquish_port = ehci_relinquish_port, 89 + .port_handed_over = ehci_port_handed_over, 90 + 91 + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 92 + }; 93 + 94 + static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev) 95 + { 96 + struct usb_hcd *hcd; 97 + struct resource *res; 98 + int ret; 99 + 100 + if (usb_disabled()) 101 + return -ENODEV; 102 + 103 + if (pdev->resource[1].flags != IORESOURCE_IRQ) { 104 + pr_debug("resource[1] is not IORESOURCE_IRQ"); 105 + return -ENOMEM; 106 + } 107 + hcd = usb_create_hcd(&ehci_sead3_hc_driver, &pdev->dev, "SEAD-3"); 108 + if (!hcd) 109 + return -ENOMEM; 110 + 111 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112 + hcd->rsrc_start = res->start; 113 + hcd->rsrc_len = resource_size(res); 114 + 115 + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 116 + pr_debug("request_mem_region failed"); 117 + ret = -EBUSY; 118 + goto err1; 119 + } 120 + 121 + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 122 + if (!hcd->regs) { 123 + pr_debug("ioremap failed"); 124 + ret = -ENOMEM; 125 + goto err2; 126 + } 127 + 128 + /* Root hub has integrated TT. */ 129 + hcd->has_tt = 1; 130 + 131 + ret = usb_add_hcd(hcd, pdev->resource[1].start, 132 + IRQF_SHARED); 133 + if (ret == 0) { 134 + platform_set_drvdata(pdev, hcd); 135 + return ret; 136 + } 137 + 138 + iounmap(hcd->regs); 139 + err2: 140 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 141 + err1: 142 + usb_put_hcd(hcd); 143 + return ret; 144 + } 145 + 146 + static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev) 147 + { 148 + struct usb_hcd *hcd = platform_get_drvdata(pdev); 149 + 150 + usb_remove_hcd(hcd); 151 + iounmap(hcd->regs); 152 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 153 + usb_put_hcd(hcd); 154 + platform_set_drvdata(pdev, NULL); 155 + 156 + return 0; 157 + } 158 + 159 + #ifdef CONFIG_PM 160 + static int ehci_hcd_sead3_drv_suspend(struct device *dev) 161 + { 162 + struct usb_hcd *hcd = dev_get_drvdata(dev); 163 + struct ehci_hcd *ehci = hcd_to_ehci(hcd); 164 + unsigned long flags; 165 + int rc = 0; 166 + 167 + if (time_before(jiffies, ehci->next_statechange)) 168 + msleep(20); 169 + 170 + /* Root hub was already suspended. Disable irq emission and 171 + * mark HW unaccessible. The PM and USB cores make sure that 172 + * the root hub is either suspended or stopped. 173 + */ 174 + ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); 175 + spin_lock_irqsave(&ehci->lock, flags); 176 + ehci_writel(ehci, 0, &ehci->regs->intr_enable); 177 + (void)ehci_readl(ehci, &ehci->regs->intr_enable); 178 + 179 + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 180 + spin_unlock_irqrestore(&ehci->lock, flags); 181 + 182 + /* could save FLADJ in case of Vaux power loss 183 + * ... we'd only use it to handle clock skew 184 + */ 185 + 186 + return rc; 187 + } 188 + 189 + static int ehci_hcd_sead3_drv_resume(struct device *dev) 190 + { 191 + struct usb_hcd *hcd = dev_get_drvdata(dev); 192 + struct ehci_hcd *ehci = hcd_to_ehci(hcd); 193 + 194 + /* maybe restore FLADJ. */ 195 + 196 + if (time_before(jiffies, ehci->next_statechange)) 197 + msleep(100); 198 + 199 + /* Mark hardware accessible again as we are out of D3 state by now */ 200 + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 201 + 202 + /* If CF is still set, we maintained PCI Vaux power. 203 + * Just undo the effect of ehci_pci_suspend(). 204 + */ 205 + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { 206 + int mask = INTR_MASK; 207 + 208 + ehci_prepare_ports_for_controller_resume(ehci); 209 + if (!hcd->self.root_hub->do_remote_wakeup) 210 + mask &= ~STS_PCD; 211 + ehci_writel(ehci, mask, &ehci->regs->intr_enable); 212 + ehci_readl(ehci, &ehci->regs->intr_enable); 213 + return 0; 214 + } 215 + 216 + ehci_dbg(ehci, "lost power, restarting\n"); 217 + usb_root_hub_lost_power(hcd->self.root_hub); 218 + 219 + /* Else reset, to cope with power loss or flush-to-storage 220 + * style "resume" having let BIOS kick in during reboot. 221 + */ 222 + (void) ehci_halt(ehci); 223 + (void) ehci_reset(ehci); 224 + 225 + /* emptying the schedule aborts any urbs */ 226 + spin_lock_irq(&ehci->lock); 227 + if (ehci->reclaim) 228 + end_unlink_async(ehci); 229 + ehci_work(ehci); 230 + spin_unlock_irq(&ehci->lock); 231 + 232 + ehci_writel(ehci, ehci->command, &ehci->regs->command); 233 + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 234 + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 235 + 236 + /* here we "know" root ports should always stay powered */ 237 + ehci_port_power(ehci, 1); 238 + 239 + ehci->rh_state = EHCI_RH_SUSPENDED; 240 + 241 + return 0; 242 + } 243 + 244 + static const struct dev_pm_ops sead3_ehci_pmops = { 245 + .suspend = ehci_hcd_sead3_drv_suspend, 246 + .resume = ehci_hcd_sead3_drv_resume, 247 + }; 248 + 249 + #define SEAD3_EHCI_PMOPS (&sead3_ehci_pmops) 250 + 251 + #else 252 + #define SEAD3_EHCI_PMOPS NULL 253 + #endif 254 + 255 + static struct platform_driver ehci_hcd_sead3_driver = { 256 + .probe = ehci_hcd_sead3_drv_probe, 257 + .remove = ehci_hcd_sead3_drv_remove, 258 + .shutdown = usb_hcd_platform_shutdown, 259 + .driver = { 260 + .name = "sead3-ehci", 261 + .owner = THIS_MODULE, 262 + .pm = SEAD3_EHCI_PMOPS, 263 + } 264 + }; 265 + 266 + MODULE_ALIAS("platform:sead3-ehci");