1 /* 2 * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 3 * Author: Chao Xie <chao.xie@marvell.com> 4 * Neil Zhang <zhangwm@marvell.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/clk.h> 16 #include <linux/err.h> 17 #include <linux/usb/otg.h> 18 #include <linux/platform_data/mv_usb.h> 19 20 #define CAPLENGTH_MASK (0xff) 21 22 struct ehci_hcd_mv { 23 struct usb_hcd *hcd; 24 25 /* Which mode does this ehci running OTG/Host ? */ 26 int mode; 27 28 void __iomem *phy_regs; 29 void __iomem *cap_regs; 30 void __iomem *op_regs; 31 32 struct usb_phy *otg; 33 34 struct mv_usb_platform_data *pdata; 35 36 struct clk *clk; 37 }; 38 39 static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) 40 { 41 clk_prepare_enable(ehci_mv->clk); 42 } 43 44 static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) 45 { 46 clk_disable_unprepare(ehci_mv->clk); 47 } 48 49 static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) 50 { 51 int retval; 52 53 ehci_clock_enable(ehci_mv); 54 if (ehci_mv->pdata->phy_init) { 55 retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs); 56 if (retval) 57 return retval; 58 } 59 60 return 0; 61 } 62 63 static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) 64 { 65 if (ehci_mv->pdata->phy_deinit) 66 ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs); 67 ehci_clock_disable(ehci_mv); 68 } 69 70 static int mv_ehci_reset(struct usb_hcd *hcd) 71 { 72 struct device *dev = hcd->self.controller; 73 struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); 74 int retval; 75 76 if (ehci_mv == NULL) { 77 dev_err(dev, "Can not find private ehci data\n"); 78 return -ENODEV; 79 } 80 81 hcd->has_tt = 1; 82 83 retval = ehci_setup(hcd); 84 if (retval) 85 dev_err(dev, "ehci_setup failed %d\n", retval); 86 87 return retval; 88 } 89 90 static const struct hc_driver mv_ehci_hc_driver = { 91 .description = hcd_name, 92 .product_desc = "Marvell EHCI", 93 .hcd_priv_size = sizeof(struct ehci_hcd), 94 95 /* 96 * generic hardware linkage 97 */ 98 .irq = ehci_irq, 99 .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, 100 101 /* 102 * basic lifecycle operations 103 */ 104 .reset = mv_ehci_reset, 105 .start = ehci_run, 106 .stop = ehci_stop, 107 .shutdown = ehci_shutdown, 108 109 /* 110 * managing i/o requests and associated device resources 111 */ 112 .urb_enqueue = ehci_urb_enqueue, 113 .urb_dequeue = ehci_urb_dequeue, 114 .endpoint_disable = ehci_endpoint_disable, 115 .endpoint_reset = ehci_endpoint_reset, 116 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 117 118 /* 119 * scheduling support 120 */ 121 .get_frame_number = ehci_get_frame, 122 123 /* 124 * root hub support 125 */ 126 .hub_status_data = ehci_hub_status_data, 127 .hub_control = ehci_hub_control, 128 .bus_suspend = ehci_bus_suspend, 129 .bus_resume = ehci_bus_resume, 130 }; 131 132 static int mv_ehci_probe(struct platform_device *pdev) 133 { 134 struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); 135 struct usb_hcd *hcd; 136 struct ehci_hcd *ehci; 137 struct ehci_hcd_mv *ehci_mv; 138 struct resource *r; 139 int retval = -ENODEV; 140 u32 offset; 141 142 if (!pdata) { 143 dev_err(&pdev->dev, "missing platform_data\n"); 144 return -ENODEV; 145 } 146 147 if (usb_disabled()) 148 return -ENODEV; 149 150 hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci"); 151 if (!hcd) 152 return -ENOMEM; 153 154 ehci_mv = devm_kzalloc(&pdev->dev, sizeof(*ehci_mv), GFP_KERNEL); 155 if (ehci_mv == NULL) { 156 retval = -ENOMEM; 157 goto err_put_hcd; 158 } 159 160 platform_set_drvdata(pdev, ehci_mv); 161 ehci_mv->pdata = pdata; 162 ehci_mv->hcd = hcd; 163 164 ehci_mv->clk = devm_clk_get(&pdev->dev, NULL); 165 if (IS_ERR(ehci_mv->clk)) { 166 dev_err(&pdev->dev, "error getting clock\n"); 167 retval = PTR_ERR(ehci_mv->clk); 168 goto err_put_hcd; 169 } 170 171 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); 172 if (r == NULL) { 173 dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); 174 retval = -ENODEV; 175 goto err_put_hcd; 176 } 177 178 ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r); 179 if (IS_ERR(ehci_mv->phy_regs)) { 180 retval = PTR_ERR(ehci_mv->phy_regs); 181 goto err_put_hcd; 182 } 183 184 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); 185 if (!r) { 186 dev_err(&pdev->dev, "no I/O memory resource defined\n"); 187 retval = -ENODEV; 188 goto err_put_hcd; 189 } 190 191 ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r); 192 if (IS_ERR(ehci_mv->cap_regs)) { 193 retval = PTR_ERR(ehci_mv->cap_regs); 194 goto err_put_hcd; 195 } 196 197 retval = mv_ehci_enable(ehci_mv); 198 if (retval) { 199 dev_err(&pdev->dev, "init phy error %d\n", retval); 200 goto err_put_hcd; 201 } 202 203 offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; 204 ehci_mv->op_regs = 205 (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); 206 207 hcd->rsrc_start = r->start; 208 hcd->rsrc_len = resource_size(r); 209 hcd->regs = ehci_mv->op_regs; 210 211 hcd->irq = platform_get_irq(pdev, 0); 212 if (!hcd->irq) { 213 dev_err(&pdev->dev, "Cannot get irq."); 214 retval = -ENODEV; 215 goto err_disable_clk; 216 } 217 218 ehci = hcd_to_ehci(hcd); 219 ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; 220 221 ehci_mv->mode = pdata->mode; 222 if (ehci_mv->mode == MV_USB_MODE_OTG) { 223 ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 224 if (IS_ERR(ehci_mv->otg)) { 225 retval = PTR_ERR(ehci_mv->otg); 226 227 if (retval == -ENXIO) 228 dev_info(&pdev->dev, "MV_USB_MODE_OTG " 229 "must have CONFIG_USB_PHY enabled\n"); 230 else 231 dev_err(&pdev->dev, 232 "unable to find transceiver\n"); 233 goto err_disable_clk; 234 } 235 236 retval = otg_set_host(ehci_mv->otg->otg, &hcd->self); 237 if (retval < 0) { 238 dev_err(&pdev->dev, 239 "unable to register with transceiver\n"); 240 retval = -ENODEV; 241 goto err_disable_clk; 242 } 243 /* otg will enable clock before use as host */ 244 mv_ehci_disable(ehci_mv); 245 } else { 246 if (pdata->set_vbus) 247 pdata->set_vbus(1); 248 249 retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); 250 if (retval) { 251 dev_err(&pdev->dev, 252 "failed to add hcd with err %d\n", retval); 253 goto err_set_vbus; 254 } 255 device_wakeup_enable(hcd->self.controller); 256 } 257 258 if (pdata->private_init) 259 pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); 260 261 dev_info(&pdev->dev, 262 "successful find EHCI device with regs 0x%p irq %d" 263 " working in %s mode\n", hcd->regs, hcd->irq, 264 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); 265 266 return 0; 267 268 err_set_vbus: 269 if (pdata->set_vbus) 270 pdata->set_vbus(0); 271 err_disable_clk: 272 mv_ehci_disable(ehci_mv); 273 err_put_hcd: 274 usb_put_hcd(hcd); 275 276 return retval; 277 } 278 279 static int mv_ehci_remove(struct platform_device *pdev) 280 { 281 struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 282 struct usb_hcd *hcd = ehci_mv->hcd; 283 284 if (hcd->rh_registered) 285 usb_remove_hcd(hcd); 286 287 if (!IS_ERR_OR_NULL(ehci_mv->otg)) 288 otg_set_host(ehci_mv->otg->otg, NULL); 289 290 if (ehci_mv->mode == MV_USB_MODE_HOST) { 291 if (ehci_mv->pdata->set_vbus) 292 ehci_mv->pdata->set_vbus(0); 293 294 mv_ehci_disable(ehci_mv); 295 } 296 297 usb_put_hcd(hcd); 298 299 return 0; 300 } 301 302 MODULE_ALIAS("mv-ehci"); 303 304 static const struct platform_device_id ehci_id_table[] = { 305 {"pxa-u2oehci", PXA_U2OEHCI}, 306 {"pxa-sph", PXA_SPH}, 307 {"mmp3-hsic", MMP3_HSIC}, 308 {"mmp3-fsic", MMP3_FSIC}, 309 {}, 310 }; 311 312 static void mv_ehci_shutdown(struct platform_device *pdev) 313 { 314 struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 315 struct usb_hcd *hcd = ehci_mv->hcd; 316 317 if (!hcd->rh_registered) 318 return; 319 320 if (hcd->driver->shutdown) 321 hcd->driver->shutdown(hcd); 322 } 323 324 static struct platform_driver ehci_mv_driver = { 325 .probe = mv_ehci_probe, 326 .remove = mv_ehci_remove, 327 .shutdown = mv_ehci_shutdown, 328 .driver = { 329 .name = "mv-ehci", 330 .bus = &platform_bus_type, 331 }, 332 .id_table = ehci_id_table, 333 }; 334