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