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 dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n"); 157 retval = -ENOMEM; 158 goto err_put_hcd; 159 } 160 161 platform_set_drvdata(pdev, ehci_mv); 162 ehci_mv->pdata = pdata; 163 ehci_mv->hcd = hcd; 164 165 ehci_mv->clk = devm_clk_get(&pdev->dev, NULL); 166 if (IS_ERR(ehci_mv->clk)) { 167 dev_err(&pdev->dev, "error getting clock\n"); 168 retval = PTR_ERR(ehci_mv->clk); 169 goto err_put_hcd; 170 } 171 172 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); 173 if (r == NULL) { 174 dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); 175 retval = -ENODEV; 176 goto err_put_hcd; 177 } 178 179 ehci_mv->phy_regs = devm_ioremap_resource(&pdev->dev, r); 180 if (IS_ERR(ehci_mv->phy_regs)) { 181 retval = PTR_ERR(ehci_mv->phy_regs); 182 goto err_put_hcd; 183 } 184 185 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); 186 if (!r) { 187 dev_err(&pdev->dev, "no I/O memory resource defined\n"); 188 retval = -ENODEV; 189 goto err_put_hcd; 190 } 191 192 ehci_mv->cap_regs = devm_ioremap_resource(&pdev->dev, r); 193 if (IS_ERR(ehci_mv->cap_regs)) { 194 retval = PTR_ERR(ehci_mv->cap_regs); 195 goto err_put_hcd; 196 } 197 198 retval = mv_ehci_enable(ehci_mv); 199 if (retval) { 200 dev_err(&pdev->dev, "init phy error %d\n", retval); 201 goto err_put_hcd; 202 } 203 204 offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; 205 ehci_mv->op_regs = 206 (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); 207 208 hcd->rsrc_start = r->start; 209 hcd->rsrc_len = resource_size(r); 210 hcd->regs = ehci_mv->op_regs; 211 212 hcd->irq = platform_get_irq(pdev, 0); 213 if (!hcd->irq) { 214 dev_err(&pdev->dev, "Cannot get irq."); 215 retval = -ENODEV; 216 goto err_disable_clk; 217 } 218 219 ehci = hcd_to_ehci(hcd); 220 ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; 221 222 ehci_mv->mode = pdata->mode; 223 if (ehci_mv->mode == MV_USB_MODE_OTG) { 224 ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); 225 if (IS_ERR(ehci_mv->otg)) { 226 retval = PTR_ERR(ehci_mv->otg); 227 228 if (retval == -ENXIO) 229 dev_info(&pdev->dev, "MV_USB_MODE_OTG " 230 "must have CONFIG_USB_PHY enabled\n"); 231 else 232 dev_err(&pdev->dev, 233 "unable to find transceiver\n"); 234 goto err_disable_clk; 235 } 236 237 retval = otg_set_host(ehci_mv->otg->otg, &hcd->self); 238 if (retval < 0) { 239 dev_err(&pdev->dev, 240 "unable to register with transceiver\n"); 241 retval = -ENODEV; 242 goto err_disable_clk; 243 } 244 /* otg will enable clock before use as host */ 245 mv_ehci_disable(ehci_mv); 246 } else { 247 if (pdata->set_vbus) 248 pdata->set_vbus(1); 249 250 retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); 251 if (retval) { 252 dev_err(&pdev->dev, 253 "failed to add hcd with err %d\n", retval); 254 goto err_set_vbus; 255 } 256 device_wakeup_enable(hcd->self.controller); 257 } 258 259 if (pdata->private_init) 260 pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); 261 262 dev_info(&pdev->dev, 263 "successful find EHCI device with regs 0x%p irq %d" 264 " working in %s mode\n", hcd->regs, hcd->irq, 265 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); 266 267 return 0; 268 269 err_set_vbus: 270 if (pdata->set_vbus) 271 pdata->set_vbus(0); 272 err_disable_clk: 273 mv_ehci_disable(ehci_mv); 274 err_put_hcd: 275 usb_put_hcd(hcd); 276 277 return retval; 278 } 279 280 static int mv_ehci_remove(struct platform_device *pdev) 281 { 282 struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 283 struct usb_hcd *hcd = ehci_mv->hcd; 284 285 if (hcd->rh_registered) 286 usb_remove_hcd(hcd); 287 288 if (!IS_ERR_OR_NULL(ehci_mv->otg)) 289 otg_set_host(ehci_mv->otg->otg, NULL); 290 291 if (ehci_mv->mode == MV_USB_MODE_HOST) { 292 if (ehci_mv->pdata->set_vbus) 293 ehci_mv->pdata->set_vbus(0); 294 295 mv_ehci_disable(ehci_mv); 296 } 297 298 usb_put_hcd(hcd); 299 300 return 0; 301 } 302 303 MODULE_ALIAS("mv-ehci"); 304 305 static const struct platform_device_id ehci_id_table[] = { 306 {"pxa-u2oehci", PXA_U2OEHCI}, 307 {"pxa-sph", PXA_SPH}, 308 {"mmp3-hsic", MMP3_HSIC}, 309 {"mmp3-fsic", MMP3_FSIC}, 310 {}, 311 }; 312 313 static void mv_ehci_shutdown(struct platform_device *pdev) 314 { 315 struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 316 struct usb_hcd *hcd = ehci_mv->hcd; 317 318 if (!hcd->rh_registered) 319 return; 320 321 if (hcd->driver->shutdown) 322 hcd->driver->shutdown(hcd); 323 } 324 325 static struct platform_driver ehci_mv_driver = { 326 .probe = mv_ehci_probe, 327 .remove = mv_ehci_remove, 328 .shutdown = mv_ehci_shutdown, 329 .driver = { 330 .name = "mv-ehci", 331 .bus = &platform_bus_type, 332 }, 333 .id_table = ehci_id_table, 334 }; 335