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; 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 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 147 ehci_mv->base = devm_ioremap_resource(&pdev->dev, r); 148 if (IS_ERR(ehci_mv->base)) { 149 retval = PTR_ERR(ehci_mv->base); 150 goto err_put_hcd; 151 } 152 153 retval = mv_ehci_enable(ehci_mv); 154 if (retval) { 155 dev_err(&pdev->dev, "init phy error %d\n", retval); 156 goto err_put_hcd; 157 } 158 159 ehci_mv->cap_regs = 160 (void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET); 161 offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; 162 ehci_mv->op_regs = 163 (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); 164 165 hcd->rsrc_start = r->start; 166 hcd->rsrc_len = resource_size(r); 167 hcd->regs = ehci_mv->op_regs; 168 169 hcd->irq = platform_get_irq(pdev, 0); 170 if (hcd->irq < 0) { 171 retval = hcd->irq; 172 goto err_disable_clk; 173 } 174 175 ehci = hcd_to_ehci(hcd); 176 ehci->caps = (struct ehci_caps __iomem *) ehci_mv->cap_regs; 177 178 if (ehci_mv->mode == MV_USB_MODE_OTG) { 179 ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 180 if (IS_ERR(ehci_mv->otg)) { 181 retval = PTR_ERR(ehci_mv->otg); 182 183 if (retval == -ENXIO) 184 dev_info(&pdev->dev, "MV_USB_MODE_OTG " 185 "must have CONFIG_USB_PHY enabled\n"); 186 else 187 dev_err(&pdev->dev, 188 "unable to find transceiver\n"); 189 goto err_disable_clk; 190 } 191 192 retval = otg_set_host(ehci_mv->otg->otg, &hcd->self); 193 if (retval < 0) { 194 dev_err(&pdev->dev, 195 "unable to register with transceiver\n"); 196 retval = -ENODEV; 197 goto err_disable_clk; 198 } 199 /* otg will enable clock before use as host */ 200 mv_ehci_disable(ehci_mv); 201 } else { 202 if (ehci_mv->set_vbus) 203 ehci_mv->set_vbus(1); 204 205 retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); 206 if (retval) { 207 dev_err(&pdev->dev, 208 "failed to add hcd with err %d\n", retval); 209 goto err_set_vbus; 210 } 211 device_wakeup_enable(hcd->self.controller); 212 } 213 214 if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC) { 215 status = ehci_readl(ehci, &ehci->regs->port_status[0]); 216 /* These "reserved" bits actually enable HSIC mode. */ 217 status |= BIT(25); 218 status &= ~GENMASK(31, 30); 219 ehci_writel(ehci, status, &ehci->regs->port_status[0]); 220 } 221 222 dev_info(&pdev->dev, 223 "successful find EHCI device with regs 0x%p irq %d" 224 " working in %s mode\n", hcd->regs, hcd->irq, 225 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); 226 227 return 0; 228 229 err_set_vbus: 230 if (ehci_mv->set_vbus) 231 ehci_mv->set_vbus(0); 232 err_disable_clk: 233 mv_ehci_disable(ehci_mv); 234 err_put_hcd: 235 usb_put_hcd(hcd); 236 237 return retval; 238 } 239 240 static int mv_ehci_remove(struct platform_device *pdev) 241 { 242 struct usb_hcd *hcd = platform_get_drvdata(pdev); 243 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); 244 245 if (hcd->rh_registered) 246 usb_remove_hcd(hcd); 247 248 if (!IS_ERR_OR_NULL(ehci_mv->otg)) 249 otg_set_host(ehci_mv->otg->otg, NULL); 250 251 if (ehci_mv->mode == MV_USB_MODE_HOST) { 252 if (ehci_mv->set_vbus) 253 ehci_mv->set_vbus(0); 254 255 mv_ehci_disable(ehci_mv); 256 } 257 258 usb_put_hcd(hcd); 259 260 return 0; 261 } 262 263 MODULE_ALIAS("mv-ehci"); 264 265 static const struct platform_device_id ehci_id_table[] = { 266 {"pxa-u2oehci", 0}, 267 {"pxa-sph", 0}, 268 {}, 269 }; 270 271 static void mv_ehci_shutdown(struct platform_device *pdev) 272 { 273 struct usb_hcd *hcd = platform_get_drvdata(pdev); 274 275 if (!hcd->rh_registered) 276 return; 277 278 if (hcd->driver->shutdown) 279 hcd->driver->shutdown(hcd); 280 } 281 282 static const struct of_device_id ehci_mv_dt_ids[] = { 283 { .compatible = "marvell,pxau2o-ehci", }, 284 {}, 285 }; 286 287 static struct platform_driver ehci_mv_driver = { 288 .probe = mv_ehci_probe, 289 .remove = mv_ehci_remove, 290 .shutdown = mv_ehci_shutdown, 291 .driver = { 292 .name = "mv-ehci", 293 .bus = &platform_bus_type, 294 .of_match_table = ehci_mv_dt_ids, 295 }, 296 .id_table = ehci_id_table, 297 }; 298 299 static int __init ehci_platform_init(void) 300 { 301 if (usb_disabled()) 302 return -ENODEV; 303 304 ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); 305 return platform_driver_register(&ehci_mv_driver); 306 } 307 module_init(ehci_platform_init); 308 309 static void __exit ehci_platform_cleanup(void) 310 { 311 platform_driver_unregister(&ehci_mv_driver); 312 } 313 module_exit(ehci_platform_cleanup); 314 315 MODULE_DESCRIPTION("Marvell EHCI driver"); 316 MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); 317 MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>"); 318 MODULE_ALIAS("mv-ehci"); 319 MODULE_LICENSE("GPL"); 320 MODULE_DEVICE_TABLE(of, ehci_mv_dt_ids); 321