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.10-rc4 321 lines 8.0 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/dma-mapping.h> 17#include <linux/io.h> 18#include <linux/kernel.h> 19#include <linux/module.h> 20#include <linux/of.h> 21#include <linux/of_gpio.h> 22#include <linux/platform_device.h> 23#include <linux/platform_data/usb-ehci-s5p.h> 24#include <linux/usb/phy.h> 25#include <linux/usb/samsung_usb_phy.h> 26#include <linux/usb.h> 27#include <linux/usb/hcd.h> 28#include <linux/usb/otg.h> 29 30#include "ehci.h" 31 32#define DRIVER_DESC "EHCI s5p driver" 33 34#define EHCI_INSNREG00(base) (base + 0x90) 35#define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25) 36#define EHCI_INSNREG00_ENA_INCR8 (0x1 << 24) 37#define EHCI_INSNREG00_ENA_INCR4 (0x1 << 23) 38#define EHCI_INSNREG00_ENA_INCRX_ALIGN (0x1 << 22) 39#define EHCI_INSNREG00_ENABLE_DMA_BURST \ 40 (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \ 41 EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN) 42 43static const char hcd_name[] = "ehci-s5p"; 44static struct hc_driver __read_mostly s5p_ehci_hc_driver; 45 46struct s5p_ehci_hcd { 47 struct clk *clk; 48 struct usb_phy *phy; 49 struct usb_otg *otg; 50 struct s5p_ehci_platdata *pdata; 51}; 52 53#define to_s5p_ehci(hcd) (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv) 54 55static void s5p_setup_vbus_gpio(struct platform_device *pdev) 56{ 57 struct device *dev = &pdev->dev; 58 int err; 59 int gpio; 60 61 if (!dev->of_node) 62 return; 63 64 gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0); 65 if (!gpio_is_valid(gpio)) 66 return; 67 68 err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, 69 "ehci_vbus_gpio"); 70 if (err) 71 dev_err(dev, "can't request ehci vbus gpio %d", gpio); 72} 73 74static int s5p_ehci_probe(struct platform_device *pdev) 75{ 76 struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; 77 struct s5p_ehci_hcd *s5p_ehci; 78 struct usb_hcd *hcd; 79 struct ehci_hcd *ehci; 80 struct resource *res; 81 struct usb_phy *phy; 82 int irq; 83 int err; 84 85 /* 86 * Right now device-tree probed devices don't get dma_mask set. 87 * Since shared usb code relies on it, set it here for now. 88 * Once we move to full device tree support this will vanish off. 89 */ 90 if (!pdev->dev.dma_mask) 91 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 92 if (!pdev->dev.coherent_dma_mask) 93 pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 94 95 s5p_setup_vbus_gpio(pdev); 96 97 hcd = usb_create_hcd(&s5p_ehci_hc_driver, 98 &pdev->dev, dev_name(&pdev->dev)); 99 if (!hcd) { 100 dev_err(&pdev->dev, "Unable to create HCD\n"); 101 return -ENOMEM; 102 } 103 s5p_ehci = to_s5p_ehci(hcd); 104 phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 105 if (IS_ERR(phy)) { 106 /* Fallback to pdata */ 107 if (!pdata) { 108 usb_put_hcd(hcd); 109 dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); 110 return -EPROBE_DEFER; 111 } else { 112 s5p_ehci->pdata = pdata; 113 } 114 } else { 115 s5p_ehci->phy = phy; 116 s5p_ehci->otg = phy->otg; 117 } 118 119 s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost"); 120 121 if (IS_ERR(s5p_ehci->clk)) { 122 dev_err(&pdev->dev, "Failed to get usbhost clock\n"); 123 err = PTR_ERR(s5p_ehci->clk); 124 goto fail_clk; 125 } 126 127 err = clk_prepare_enable(s5p_ehci->clk); 128 if (err) 129 goto fail_clk; 130 131 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 132 if (!res) { 133 dev_err(&pdev->dev, "Failed to get I/O memory\n"); 134 err = -ENXIO; 135 goto fail_io; 136 } 137 138 hcd->rsrc_start = res->start; 139 hcd->rsrc_len = resource_size(res); 140 hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len); 141 if (!hcd->regs) { 142 dev_err(&pdev->dev, "Failed to remap I/O memory\n"); 143 err = -ENOMEM; 144 goto fail_io; 145 } 146 147 irq = platform_get_irq(pdev, 0); 148 if (!irq) { 149 dev_err(&pdev->dev, "Failed to get IRQ\n"); 150 err = -ENODEV; 151 goto fail_io; 152 } 153 154 if (s5p_ehci->otg) 155 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); 156 157 if (s5p_ehci->phy) 158 usb_phy_init(s5p_ehci->phy); 159 else if (s5p_ehci->pdata->phy_init) 160 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); 161 162 ehci = hcd_to_ehci(hcd); 163 ehci->caps = hcd->regs; 164 165 /* DMA burst Enable */ 166 writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); 167 168 err = usb_add_hcd(hcd, irq, IRQF_SHARED); 169 if (err) { 170 dev_err(&pdev->dev, "Failed to add USB HCD\n"); 171 goto fail_add_hcd; 172 } 173 174 platform_set_drvdata(pdev, hcd); 175 176 return 0; 177 178fail_add_hcd: 179 if (s5p_ehci->phy) 180 usb_phy_shutdown(s5p_ehci->phy); 181 else if (s5p_ehci->pdata->phy_exit) 182 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); 183fail_io: 184 clk_disable_unprepare(s5p_ehci->clk); 185fail_clk: 186 usb_put_hcd(hcd); 187 return err; 188} 189 190static int s5p_ehci_remove(struct platform_device *pdev) 191{ 192 struct usb_hcd *hcd = platform_get_drvdata(pdev); 193 struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); 194 195 usb_remove_hcd(hcd); 196 197 if (s5p_ehci->otg) 198 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); 199 200 if (s5p_ehci->phy) 201 usb_phy_shutdown(s5p_ehci->phy); 202 else if (s5p_ehci->pdata->phy_exit) 203 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); 204 205 clk_disable_unprepare(s5p_ehci->clk); 206 207 usb_put_hcd(hcd); 208 209 return 0; 210} 211 212static void s5p_ehci_shutdown(struct platform_device *pdev) 213{ 214 struct usb_hcd *hcd = platform_get_drvdata(pdev); 215 216 if (hcd->driver->shutdown) 217 hcd->driver->shutdown(hcd); 218} 219 220#ifdef CONFIG_PM 221static int s5p_ehci_suspend(struct device *dev) 222{ 223 struct usb_hcd *hcd = dev_get_drvdata(dev); 224 struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); 225 struct platform_device *pdev = to_platform_device(dev); 226 227 bool do_wakeup = device_may_wakeup(dev); 228 int rc; 229 230 rc = ehci_suspend(hcd, do_wakeup); 231 232 if (s5p_ehci->otg) 233 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); 234 235 if (s5p_ehci->phy) 236 usb_phy_shutdown(s5p_ehci->phy); 237 else if (s5p_ehci->pdata->phy_exit) 238 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); 239 240 clk_disable_unprepare(s5p_ehci->clk); 241 242 return rc; 243} 244 245static int s5p_ehci_resume(struct device *dev) 246{ 247 struct usb_hcd *hcd = dev_get_drvdata(dev); 248 struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd); 249 struct platform_device *pdev = to_platform_device(dev); 250 251 clk_prepare_enable(s5p_ehci->clk); 252 253 if (s5p_ehci->otg) 254 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self); 255 256 if (s5p_ehci->phy) 257 usb_phy_init(s5p_ehci->phy); 258 else if (s5p_ehci->pdata->phy_init) 259 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); 260 261 /* DMA burst Enable */ 262 writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); 263 264 ehci_resume(hcd, false); 265 return 0; 266} 267#else 268#define s5p_ehci_suspend NULL 269#define s5p_ehci_resume NULL 270#endif 271 272static const struct dev_pm_ops s5p_ehci_pm_ops = { 273 .suspend = s5p_ehci_suspend, 274 .resume = s5p_ehci_resume, 275}; 276 277#ifdef CONFIG_OF 278static const struct of_device_id exynos_ehci_match[] = { 279 { .compatible = "samsung,exynos4210-ehci" }, 280 {}, 281}; 282MODULE_DEVICE_TABLE(of, exynos_ehci_match); 283#endif 284 285static struct platform_driver s5p_ehci_driver = { 286 .probe = s5p_ehci_probe, 287 .remove = s5p_ehci_remove, 288 .shutdown = s5p_ehci_shutdown, 289 .driver = { 290 .name = "s5p-ehci", 291 .owner = THIS_MODULE, 292 .pm = &s5p_ehci_pm_ops, 293 .of_match_table = of_match_ptr(exynos_ehci_match), 294 } 295}; 296static const struct ehci_driver_overrides s5p_overrides __initdata = { 297 .extra_priv_size = sizeof(struct s5p_ehci_hcd), 298}; 299 300static int __init ehci_s5p_init(void) 301{ 302 if (usb_disabled()) 303 return -ENODEV; 304 305 pr_info("%s: " DRIVER_DESC "\n", hcd_name); 306 ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides); 307 return platform_driver_register(&s5p_ehci_driver); 308} 309module_init(ehci_s5p_init); 310 311static void __exit ehci_s5p_cleanup(void) 312{ 313 platform_driver_unregister(&s5p_ehci_driver); 314} 315module_exit(ehci_s5p_cleanup); 316 317MODULE_DESCRIPTION(DRIVER_DESC); 318MODULE_ALIAS("platform:s5p-ehci"); 319MODULE_AUTHOR("Jingoo Han"); 320MODULE_AUTHOR("Joonyoung Shim"); 321MODULE_LICENSE("GPL v2");