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

Configure Feed

Select the types of activity you want to include in your feed.

at v3.5-rc5 317 lines 7.9 kB view raw
1/* 2 * SAMSUNG S5P USB HOST EHCI Controller 3 * 4 * Copyright (C) 2011 Samsung Electronics Co.Ltd 5 * Author: Jingoo Han <jg1.han@samsung.com> 6 * Author: Joonyoung Shim <jy0922.shim@samsung.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 * 13 */ 14 15#include <linux/clk.h> 16#include <linux/platform_device.h> 17#include <plat/ehci.h> 18#include <plat/usb-phy.h> 19 20#define EHCI_INSNREG00(base) (base + 0x90) 21#define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) 22#define EHCI_INSNREG00_ENA_INCR8 (0x1 << 24) 23#define EHCI_INSNREG00_ENA_INCR4 (0x1 << 23) 24#define EHCI_INSNREG00_ENA_INCRX_ALIGN (0x1 << 22) 25#define EHCI_INSNREG00_ENABLE_DMA_BURST \ 26 (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \ 27 EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN) 28 29struct s5p_ehci_hcd { 30 struct device *dev; 31 struct usb_hcd *hcd; 32 struct clk *clk; 33}; 34 35static const struct hc_driver s5p_ehci_hc_driver = { 36 .description = hcd_name, 37 .product_desc = "S5P EHCI Host Controller", 38 .hcd_priv_size = sizeof(struct ehci_hcd), 39 40 .irq = ehci_irq, 41 .flags = HCD_MEMORY | HCD_USB2, 42 43 .reset = ehci_init, 44 .start = ehci_run, 45 .stop = ehci_stop, 46 .shutdown = ehci_shutdown, 47 48 .get_frame_number = ehci_get_frame, 49 50 .urb_enqueue = ehci_urb_enqueue, 51 .urb_dequeue = ehci_urb_dequeue, 52 .endpoint_disable = ehci_endpoint_disable, 53 .endpoint_reset = ehci_endpoint_reset, 54 55 .hub_status_data = ehci_hub_status_data, 56 .hub_control = ehci_hub_control, 57 .bus_suspend = ehci_bus_suspend, 58 .bus_resume = ehci_bus_resume, 59 60 .relinquish_port = ehci_relinquish_port, 61 .port_handed_over = ehci_port_handed_over, 62 63 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 64}; 65 66static int __devinit s5p_ehci_probe(struct platform_device *pdev) 67{ 68 struct s5p_ehci_platdata *pdata; 69 struct s5p_ehci_hcd *s5p_ehci; 70 struct usb_hcd *hcd; 71 struct ehci_hcd *ehci; 72 struct resource *res; 73 int irq; 74 int err; 75 76 pdata = pdev->dev.platform_data; 77 if (!pdata) { 78 dev_err(&pdev->dev, "No platform data defined\n"); 79 return -EINVAL; 80 } 81 82 s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL); 83 if (!s5p_ehci) 84 return -ENOMEM; 85 86 s5p_ehci->dev = &pdev->dev; 87 88 hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, 89 dev_name(&pdev->dev)); 90 if (!hcd) { 91 dev_err(&pdev->dev, "Unable to create HCD\n"); 92 err = -ENOMEM; 93 goto fail_hcd; 94 } 95 96 s5p_ehci->hcd = hcd; 97 s5p_ehci->clk = clk_get(&pdev->dev, "usbhost"); 98 99 if (IS_ERR(s5p_ehci->clk)) { 100 dev_err(&pdev->dev, "Failed to get usbhost clock\n"); 101 err = PTR_ERR(s5p_ehci->clk); 102 goto fail_clk; 103 } 104 105 err = clk_enable(s5p_ehci->clk); 106 if (err) 107 goto fail_clken; 108 109 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 110 if (!res) { 111 dev_err(&pdev->dev, "Failed to get I/O memory\n"); 112 err = -ENXIO; 113 goto fail_io; 114 } 115 116 hcd->rsrc_start = res->start; 117 hcd->rsrc_len = resource_size(res); 118 hcd->regs = ioremap(res->start, resource_size(res)); 119 if (!hcd->regs) { 120 dev_err(&pdev->dev, "Failed to remap I/O memory\n"); 121 err = -ENOMEM; 122 goto fail_io; 123 } 124 125 irq = platform_get_irq(pdev, 0); 126 if (!irq) { 127 dev_err(&pdev->dev, "Failed to get IRQ\n"); 128 err = -ENODEV; 129 goto fail; 130 } 131 132 if (pdata->phy_init) 133 pdata->phy_init(pdev, S5P_USB_PHY_HOST); 134 135 ehci = hcd_to_ehci(hcd); 136 ehci->caps = hcd->regs; 137 ehci->regs = hcd->regs + 138 HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); 139 140 /* DMA burst Enable */ 141 writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); 142 143 dbg_hcs_params(ehci, "reset"); 144 dbg_hcc_params(ehci, "reset"); 145 146 /* cache this readonly data; minimize chip reads */ 147 ehci->hcs_params = readl(&ehci->caps->hcs_params); 148 149 ehci_reset(ehci); 150 151 err = usb_add_hcd(hcd, irq, IRQF_SHARED); 152 if (err) { 153 dev_err(&pdev->dev, "Failed to add USB HCD\n"); 154 goto fail; 155 } 156 157 platform_set_drvdata(pdev, s5p_ehci); 158 159 return 0; 160 161fail: 162 iounmap(hcd->regs); 163fail_io: 164 clk_disable(s5p_ehci->clk); 165fail_clken: 166 clk_put(s5p_ehci->clk); 167fail_clk: 168 usb_put_hcd(hcd); 169fail_hcd: 170 kfree(s5p_ehci); 171 return err; 172} 173 174static int __devexit s5p_ehci_remove(struct platform_device *pdev) 175{ 176 struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; 177 struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); 178 struct usb_hcd *hcd = s5p_ehci->hcd; 179 180 usb_remove_hcd(hcd); 181 182 if (pdata && pdata->phy_exit) 183 pdata->phy_exit(pdev, S5P_USB_PHY_HOST); 184 185 iounmap(hcd->regs); 186 187 clk_disable(s5p_ehci->clk); 188 clk_put(s5p_ehci->clk); 189 190 usb_put_hcd(hcd); 191 kfree(s5p_ehci); 192 193 return 0; 194} 195 196static void s5p_ehci_shutdown(struct platform_device *pdev) 197{ 198 struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); 199 struct usb_hcd *hcd = s5p_ehci->hcd; 200 201 if (hcd->driver->shutdown) 202 hcd->driver->shutdown(hcd); 203} 204 205#ifdef CONFIG_PM 206static int s5p_ehci_suspend(struct device *dev) 207{ 208 struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); 209 struct usb_hcd *hcd = s5p_ehci->hcd; 210 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 211 struct platform_device *pdev = to_platform_device(dev); 212 struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; 213 unsigned long flags; 214 int rc = 0; 215 216 if (time_before(jiffies, ehci->next_statechange)) 217 msleep(20); 218 219 /* 220 * Root hub was already suspended. Disable irq emission and 221 * mark HW unaccessible. The PM and USB cores make sure that 222 * the root hub is either suspended or stopped. 223 */ 224 ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); 225 spin_lock_irqsave(&ehci->lock, flags); 226 ehci_writel(ehci, 0, &ehci->regs->intr_enable); 227 (void)ehci_readl(ehci, &ehci->regs->intr_enable); 228 229 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 230 spin_unlock_irqrestore(&ehci->lock, flags); 231 232 if (pdata && pdata->phy_exit) 233 pdata->phy_exit(pdev, S5P_USB_PHY_HOST); 234 235 clk_disable(s5p_ehci->clk); 236 237 return rc; 238} 239 240static int s5p_ehci_resume(struct device *dev) 241{ 242 struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); 243 struct usb_hcd *hcd = s5p_ehci->hcd; 244 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 245 struct platform_device *pdev = to_platform_device(dev); 246 struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; 247 248 clk_enable(s5p_ehci->clk); 249 250 if (pdata && pdata->phy_init) 251 pdata->phy_init(pdev, S5P_USB_PHY_HOST); 252 253 /* DMA burst Enable */ 254 writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); 255 256 if (time_before(jiffies, ehci->next_statechange)) 257 msleep(100); 258 259 /* Mark hardware accessible again as we are out of D3 state by now */ 260 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); 261 262 if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { 263 int mask = INTR_MASK; 264 265 ehci_prepare_ports_for_controller_resume(ehci); 266 if (!hcd->self.root_hub->do_remote_wakeup) 267 mask &= ~STS_PCD; 268 ehci_writel(ehci, mask, &ehci->regs->intr_enable); 269 ehci_readl(ehci, &ehci->regs->intr_enable); 270 return 0; 271 } 272 273 usb_root_hub_lost_power(hcd->self.root_hub); 274 275 (void) ehci_halt(ehci); 276 (void) ehci_reset(ehci); 277 278 /* emptying the schedule aborts any urbs */ 279 spin_lock_irq(&ehci->lock); 280 if (ehci->reclaim) 281 end_unlink_async(ehci); 282 ehci_work(ehci); 283 spin_unlock_irq(&ehci->lock); 284 285 ehci_writel(ehci, ehci->command, &ehci->regs->command); 286 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 287 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 288 289 /* here we "know" root ports should always stay powered */ 290 ehci_port_power(ehci, 1); 291 292 ehci->rh_state = EHCI_RH_SUSPENDED; 293 294 return 0; 295} 296#else 297#define s5p_ehci_suspend NULL 298#define s5p_ehci_resume NULL 299#endif 300 301static const struct dev_pm_ops s5p_ehci_pm_ops = { 302 .suspend = s5p_ehci_suspend, 303 .resume = s5p_ehci_resume, 304}; 305 306static struct platform_driver s5p_ehci_driver = { 307 .probe = s5p_ehci_probe, 308 .remove = __devexit_p(s5p_ehci_remove), 309 .shutdown = s5p_ehci_shutdown, 310 .driver = { 311 .name = "s5p-ehci", 312 .owner = THIS_MODULE, 313 .pm = &s5p_ehci_pm_ops, 314 } 315}; 316 317MODULE_ALIAS("platform:s5p-ehci");