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