1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 4 * Author: Chao Xie <chao.xie@marvell.com> 5 * Neil Zhang <zhangwm@marvell.com> 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/platform_device.h> 11 #include <linux/clk.h> 12 #include <linux/err.h> 13 #include <linux/usb/otg.h> 14 #include <linux/platform_data/mv_usb.h> 15 #include <linux/io.h> 16 17 #include <linux/usb/hcd.h> 18 19 #include "ehci.h" 20 21 #define CAPLENGTH_MASK (0xff) 22 23 #define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv) 24 25 struct ehci_hcd_mv { 26 /* Which mode does this ehci running OTG/Host ? */ 27 int mode; 28 29 void __iomem *phy_regs; 30 void __iomem *cap_regs; 31 void __iomem *op_regs; 32 33 struct usb_phy *otg; 34 35 struct mv_usb_platform_data *pdata; 36 37 struct clk *clk; 38 }; 39 40 static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) 41 { 42 clk_prepare_enable(ehci_mv->clk); 43 } 44 45 static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) 46 { 47 clk_disable_unprepare(ehci_mv->clk); 48 } 49 50 static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) 51 { 52 int retval; 53 54 ehci_clock_enable(ehci_mv); 55 if (ehci_mv->pdata->phy_init) { 56 retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs); 57 if (retval) 58 return retval; 59 } 60 61 return 0; 62 } 63 64 static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) 65 { 66 if (ehci_mv->pdata->phy_deinit) 67 ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs); 68 ehci_clock_disable(ehci_mv); 69 } 70 71 static int mv_ehci_reset(struct usb_hcd *hcd) 72 { 73 struct device *dev = hcd->self.controller; 74 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); 75 int retval; 76 77 if (ehci_mv == NULL) { 78 dev_err(dev, "Can not find private ehci data\n"); 79 return -ENODEV; 80 } 81 82 hcd->has_tt = 1; 83 84 retval = ehci_setup(hcd); 85 if (retval) 86 dev_err(dev, "ehci_setup failed %d\n", retval); 87 88 return retval; 89 } 90 91 static struct hc_driver __read_mostly ehci_platform_hc_driver; 92 93 static const struct ehci_driver_overrides platform_overrides __initconst = { 94 .reset = mv_ehci_reset, 95 .extra_priv_size = sizeof(struct ehci_hcd_mv), 96 }; 97 98 static int mv_ehci_probe(struct platform_device *pdev) 99 { 100 struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); 101 struct usb_hcd *hcd; 102 struct ehci_hcd *ehci; 103 struct ehci_hcd_mv *ehci_mv; 104 struct resource *r; 105 int retval = -ENODEV; 106 u32 offset; 107 108 if (!pdata) { 109 dev_err(&pdev->dev, "missing platform_data\n"); 110 return -ENODEV; 111 } 112 113 if (usb_disabled()) 114 return -ENODEV; 115 116 hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci"); 117 if (!hcd) 118 return -ENOMEM; 119 120 platform_set_drvdata(pdev, hcd); 121 ehci_mv = hcd_to_ehci_hcd_mv(hcd); 122 ehci_mv->pdata = pdata; 123 124 ehci_mv->clk = devm_clk_get(&pdev->dev, NULL); 125 if (IS_ERR(ehci_mv->clk)) { 126 dev_err(&pdev->dev, "error getting clock\n"); 127 retval = PTR_ERR(ehci_mv->clk); 128 goto err_put_hcd; 129 } 130 131 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); 132 ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r); 133 if (IS_ERR(ehci_mv->phy_regs)) { 134 retval = PTR_ERR(ehci_mv->phy_regs); 135 goto err_put_hcd; 136 } 137 138 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); 139 ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r); 140 if (IS_ERR(ehci_mv->cap_regs)) { 141 retval = PTR_ERR(ehci_mv->cap_regs); 142 goto err_put_hcd; 143 } 144 145 retval = mv_ehci_enable(ehci_mv); 146 if (retval) { 147 dev_err(&pdev->dev, "init phy error %d\n", retval); 148 goto err_put_hcd; 149 } 150 151 offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; 152 ehci_mv->op_regs = 153 (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); 154 155 hcd->rsrc_start = r->start; 156 hcd->rsrc_len = resource_size(r); 157 hcd->regs = ehci_mv->op_regs; 158 159 hcd->irq = platform_get_irq(pdev, 0); 160 if (!hcd->irq) { 161 dev_err(&pdev->dev, "Cannot get irq."); 162 retval = -ENODEV; 163 goto err_disable_clk; 164 } 165 166 ehci = hcd_to_ehci(hcd); 167 ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; 168 169 ehci_mv->mode = pdata->mode; 170 if (ehci_mv->mode == MV_USB_MODE_OTG) { 171 ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 172 if (IS_ERR(ehci_mv->otg)) { 173 retval = PTR_ERR(ehci_mv->otg); 174 175 if (retval == -ENXIO) 176 dev_info(&pdev->dev, "MV_USB_MODE_OTG " 177 "must have CONFIG_USB_PHY enabled\n"); 178 else 179 dev_err(&pdev->dev, 180 "unable to find transceiver\n"); 181 goto err_disable_clk; 182 } 183 184 retval = otg_set_host(ehci_mv->otg->otg, &hcd->self); 185 if (retval < 0) { 186 dev_err(&pdev->dev, 187 "unable to register with transceiver\n"); 188 retval = -ENODEV; 189 goto err_disable_clk; 190 } 191 /* otg will enable clock before use as host */ 192 mv_ehci_disable(ehci_mv); 193 } else { 194 if (pdata->set_vbus) 195 pdata->set_vbus(1); 196 197 retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); 198 if (retval) { 199 dev_err(&pdev->dev, 200 "failed to add hcd with err %d\n", retval); 201 goto err_set_vbus; 202 } 203 device_wakeup_enable(hcd->self.controller); 204 } 205 206 if (pdata->private_init) 207 pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); 208 209 dev_info(&pdev->dev, 210 "successful find EHCI device with regs 0x%p irq %d" 211 " working in %s mode\n", hcd->regs, hcd->irq, 212 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); 213 214 return 0; 215 216 err_set_vbus: 217 if (pdata->set_vbus) 218 pdata->set_vbus(0); 219 err_disable_clk: 220 mv_ehci_disable(ehci_mv); 221 err_put_hcd: 222 usb_put_hcd(hcd); 223 224 return retval; 225 } 226 227 static int mv_ehci_remove(struct platform_device *pdev) 228 { 229 struct usb_hcd *hcd = platform_get_drvdata(pdev); 230 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); 231 232 if (hcd->rh_registered) 233 usb_remove_hcd(hcd); 234 235 if (!IS_ERR_OR_NULL(ehci_mv->otg)) 236 otg_set_host(ehci_mv->otg->otg, NULL); 237 238 if (ehci_mv->mode == MV_USB_MODE_HOST) { 239 if (ehci_mv->pdata->set_vbus) 240 ehci_mv->pdata->set_vbus(0); 241 242 mv_ehci_disable(ehci_mv); 243 } 244 245 usb_put_hcd(hcd); 246 247 return 0; 248 } 249 250 MODULE_ALIAS("mv-ehci"); 251 252 static const struct platform_device_id ehci_id_table[] = { 253 {"pxa-u2oehci", PXA_U2OEHCI}, 254 {"pxa-sph", PXA_SPH}, 255 {"mmp3-hsic", MMP3_HSIC}, 256 {"mmp3-fsic", MMP3_FSIC}, 257 {}, 258 }; 259 260 static void mv_ehci_shutdown(struct platform_device *pdev) 261 { 262 struct usb_hcd *hcd = platform_get_drvdata(pdev); 263 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); 264 265 if (!hcd->rh_registered) 266 return; 267 268 if (hcd->driver->shutdown) 269 hcd->driver->shutdown(hcd); 270 } 271 272 static struct platform_driver ehci_mv_driver = { 273 .probe = mv_ehci_probe, 274 .remove = mv_ehci_remove, 275 .shutdown = mv_ehci_shutdown, 276 .driver = { 277 .name = "mv-ehci", 278 .bus = &platform_bus_type, 279 }, 280 .id_table = ehci_id_table, 281 }; 282 283 static int __init ehci_platform_init(void) 284 { 285 if (usb_disabled()) 286 return -ENODEV; 287 288 ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); 289 return platform_driver_register(&ehci_mv_driver); 290 } 291 module_init(ehci_platform_init); 292 293 static void __exit ehci_platform_cleanup(void) 294 { 295 platform_driver_unregister(&ehci_mv_driver); 296 } 297 module_exit(ehci_platform_cleanup); 298 299 MODULE_DESCRIPTION("Marvell EHCI driver"); 300 MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); 301 MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>"); 302 MODULE_ALIAS("mv-ehci"); 303 MODULE_LICENSE("GPL"); 304