13a082ec9SNeil Zhang /* 23a082ec9SNeil Zhang * Copyright (C) 2011 Marvell International Ltd. All rights reserved. 33a082ec9SNeil Zhang * Author: Chao Xie <chao.xie@marvell.com> 43a082ec9SNeil Zhang * Neil Zhang <zhangwm@marvell.com> 53a082ec9SNeil Zhang * 63a082ec9SNeil Zhang * This program is free software; you can redistribute it and/or modify it 73a082ec9SNeil Zhang * under the terms of the GNU General Public License as published by the 83a082ec9SNeil Zhang * Free Software Foundation; either version 2 of the License, or (at your 93a082ec9SNeil Zhang * option) any later version. 103a082ec9SNeil Zhang */ 113a082ec9SNeil Zhang 123a082ec9SNeil Zhang #include <linux/kernel.h> 133a082ec9SNeil Zhang #include <linux/module.h> 143a082ec9SNeil Zhang #include <linux/platform_device.h> 153a082ec9SNeil Zhang #include <linux/clk.h> 163a082ec9SNeil Zhang #include <linux/usb/otg.h> 173a082ec9SNeil Zhang #include <linux/platform_data/mv_usb.h> 183a082ec9SNeil Zhang 193a082ec9SNeil Zhang #define CAPLENGTH_MASK (0xff) 203a082ec9SNeil Zhang 213a082ec9SNeil Zhang struct ehci_hcd_mv { 223a082ec9SNeil Zhang struct usb_hcd *hcd; 233a082ec9SNeil Zhang 243a082ec9SNeil Zhang /* Which mode does this ehci running OTG/Host ? */ 253a082ec9SNeil Zhang int mode; 263a082ec9SNeil Zhang 273a082ec9SNeil Zhang void __iomem *phy_regs; 283a082ec9SNeil Zhang void __iomem *cap_regs; 293a082ec9SNeil Zhang void __iomem *op_regs; 303a082ec9SNeil Zhang 31*86753811SHeikki Krogerus struct usb_phy *otg; 323a082ec9SNeil Zhang 333a082ec9SNeil Zhang struct mv_usb_platform_data *pdata; 343a082ec9SNeil Zhang 353a082ec9SNeil Zhang /* clock source and total clock number */ 363a082ec9SNeil Zhang unsigned int clknum; 373a082ec9SNeil Zhang struct clk *clk[0]; 383a082ec9SNeil Zhang }; 393a082ec9SNeil Zhang 403a082ec9SNeil Zhang static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv) 413a082ec9SNeil Zhang { 423a082ec9SNeil Zhang unsigned int i; 433a082ec9SNeil Zhang 443a082ec9SNeil Zhang for (i = 0; i < ehci_mv->clknum; i++) 453a082ec9SNeil Zhang clk_enable(ehci_mv->clk[i]); 463a082ec9SNeil Zhang } 473a082ec9SNeil Zhang 483a082ec9SNeil Zhang static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv) 493a082ec9SNeil Zhang { 503a082ec9SNeil Zhang unsigned int i; 513a082ec9SNeil Zhang 523a082ec9SNeil Zhang for (i = 0; i < ehci_mv->clknum; i++) 533a082ec9SNeil Zhang clk_disable(ehci_mv->clk[i]); 543a082ec9SNeil Zhang } 553a082ec9SNeil Zhang 563a082ec9SNeil Zhang static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv) 573a082ec9SNeil Zhang { 583a082ec9SNeil Zhang int retval; 593a082ec9SNeil Zhang 603a082ec9SNeil Zhang ehci_clock_enable(ehci_mv); 613a082ec9SNeil Zhang if (ehci_mv->pdata->phy_init) { 623a082ec9SNeil Zhang retval = ehci_mv->pdata->phy_init(ehci_mv->phy_regs); 633a082ec9SNeil Zhang if (retval) 643a082ec9SNeil Zhang return retval; 653a082ec9SNeil Zhang } 663a082ec9SNeil Zhang 673a082ec9SNeil Zhang return 0; 683a082ec9SNeil Zhang } 693a082ec9SNeil Zhang 703a082ec9SNeil Zhang static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv) 713a082ec9SNeil Zhang { 723a082ec9SNeil Zhang if (ehci_mv->pdata->phy_deinit) 733a082ec9SNeil Zhang ehci_mv->pdata->phy_deinit(ehci_mv->phy_regs); 743a082ec9SNeil Zhang ehci_clock_disable(ehci_mv); 753a082ec9SNeil Zhang } 763a082ec9SNeil Zhang 773a082ec9SNeil Zhang static int mv_ehci_reset(struct usb_hcd *hcd) 783a082ec9SNeil Zhang { 793a082ec9SNeil Zhang struct ehci_hcd *ehci = hcd_to_ehci(hcd); 803a082ec9SNeil Zhang struct device *dev = hcd->self.controller; 813a082ec9SNeil Zhang struct ehci_hcd_mv *ehci_mv = dev_get_drvdata(dev); 823a082ec9SNeil Zhang int retval; 833a082ec9SNeil Zhang 843a082ec9SNeil Zhang if (ehci_mv == NULL) { 853a082ec9SNeil Zhang dev_err(dev, "Can not find private ehci data\n"); 863a082ec9SNeil Zhang return -ENODEV; 873a082ec9SNeil Zhang } 883a082ec9SNeil Zhang 893a082ec9SNeil Zhang /* 903a082ec9SNeil Zhang * data structure init 913a082ec9SNeil Zhang */ 923a082ec9SNeil Zhang retval = ehci_init(hcd); 933a082ec9SNeil Zhang if (retval) { 943a082ec9SNeil Zhang dev_err(dev, "ehci_init failed %d\n", retval); 953a082ec9SNeil Zhang return retval; 963a082ec9SNeil Zhang } 973a082ec9SNeil Zhang 983a082ec9SNeil Zhang hcd->has_tt = 1; 993a082ec9SNeil Zhang ehci->sbrn = 0x20; 1003a082ec9SNeil Zhang 1013a082ec9SNeil Zhang retval = ehci_reset(ehci); 1023a082ec9SNeil Zhang if (retval) { 1033a082ec9SNeil Zhang dev_err(dev, "ehci_reset failed %d\n", retval); 1043a082ec9SNeil Zhang return retval; 1053a082ec9SNeil Zhang } 1063a082ec9SNeil Zhang 1073a082ec9SNeil Zhang return 0; 1083a082ec9SNeil Zhang } 1093a082ec9SNeil Zhang 1103a082ec9SNeil Zhang static const struct hc_driver mv_ehci_hc_driver = { 1113a082ec9SNeil Zhang .description = hcd_name, 1123a082ec9SNeil Zhang .product_desc = "Marvell EHCI", 1133a082ec9SNeil Zhang .hcd_priv_size = sizeof(struct ehci_hcd), 1143a082ec9SNeil Zhang 1153a082ec9SNeil Zhang /* 1163a082ec9SNeil Zhang * generic hardware linkage 1173a082ec9SNeil Zhang */ 1183a082ec9SNeil Zhang .irq = ehci_irq, 1193a082ec9SNeil Zhang .flags = HCD_MEMORY | HCD_USB2, 1203a082ec9SNeil Zhang 1213a082ec9SNeil Zhang /* 1223a082ec9SNeil Zhang * basic lifecycle operations 1233a082ec9SNeil Zhang */ 1243a082ec9SNeil Zhang .reset = mv_ehci_reset, 1253a082ec9SNeil Zhang .start = ehci_run, 1263a082ec9SNeil Zhang .stop = ehci_stop, 1273a082ec9SNeil Zhang .shutdown = ehci_shutdown, 1283a082ec9SNeil Zhang 1293a082ec9SNeil Zhang /* 1303a082ec9SNeil Zhang * managing i/o requests and associated device resources 1313a082ec9SNeil Zhang */ 1323a082ec9SNeil Zhang .urb_enqueue = ehci_urb_enqueue, 1333a082ec9SNeil Zhang .urb_dequeue = ehci_urb_dequeue, 1343a082ec9SNeil Zhang .endpoint_disable = ehci_endpoint_disable, 1353a082ec9SNeil Zhang .endpoint_reset = ehci_endpoint_reset, 1363a082ec9SNeil Zhang .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 1373a082ec9SNeil Zhang 1383a082ec9SNeil Zhang /* 1393a082ec9SNeil Zhang * scheduling support 1403a082ec9SNeil Zhang */ 1413a082ec9SNeil Zhang .get_frame_number = ehci_get_frame, 1423a082ec9SNeil Zhang 1433a082ec9SNeil Zhang /* 1443a082ec9SNeil Zhang * root hub support 1453a082ec9SNeil Zhang */ 1463a082ec9SNeil Zhang .hub_status_data = ehci_hub_status_data, 1473a082ec9SNeil Zhang .hub_control = ehci_hub_control, 1483a082ec9SNeil Zhang .bus_suspend = ehci_bus_suspend, 1493a082ec9SNeil Zhang .bus_resume = ehci_bus_resume, 1503a082ec9SNeil Zhang }; 1513a082ec9SNeil Zhang 1523a082ec9SNeil Zhang static int mv_ehci_probe(struct platform_device *pdev) 1533a082ec9SNeil Zhang { 1543a082ec9SNeil Zhang struct mv_usb_platform_data *pdata = pdev->dev.platform_data; 1553a082ec9SNeil Zhang struct usb_hcd *hcd; 1563a082ec9SNeil Zhang struct ehci_hcd *ehci; 1573a082ec9SNeil Zhang struct ehci_hcd_mv *ehci_mv; 1583a082ec9SNeil Zhang struct resource *r; 1593a082ec9SNeil Zhang int clk_i, retval = -ENODEV; 1603a082ec9SNeil Zhang u32 offset; 1613a082ec9SNeil Zhang size_t size; 1623a082ec9SNeil Zhang 1633a082ec9SNeil Zhang if (!pdata) { 1643a082ec9SNeil Zhang dev_err(&pdev->dev, "missing platform_data\n"); 1653a082ec9SNeil Zhang return -ENODEV; 1663a082ec9SNeil Zhang } 1673a082ec9SNeil Zhang 1683a082ec9SNeil Zhang if (usb_disabled()) 1693a082ec9SNeil Zhang return -ENODEV; 1703a082ec9SNeil Zhang 1713a082ec9SNeil Zhang hcd = usb_create_hcd(&mv_ehci_hc_driver, &pdev->dev, "mv ehci"); 1723a082ec9SNeil Zhang if (!hcd) 1733a082ec9SNeil Zhang return -ENOMEM; 1743a082ec9SNeil Zhang 1753a082ec9SNeil Zhang size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum; 1763a082ec9SNeil Zhang ehci_mv = kzalloc(size, GFP_KERNEL); 1773a082ec9SNeil Zhang if (ehci_mv == NULL) { 1783a082ec9SNeil Zhang dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n"); 1793a082ec9SNeil Zhang retval = -ENOMEM; 1803a082ec9SNeil Zhang goto err_put_hcd; 1813a082ec9SNeil Zhang } 1823a082ec9SNeil Zhang 1833a082ec9SNeil Zhang platform_set_drvdata(pdev, ehci_mv); 1843a082ec9SNeil Zhang ehci_mv->pdata = pdata; 1853a082ec9SNeil Zhang ehci_mv->hcd = hcd; 1863a082ec9SNeil Zhang 1873a082ec9SNeil Zhang ehci_mv->clknum = pdata->clknum; 1883a082ec9SNeil Zhang for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) { 1893a082ec9SNeil Zhang ehci_mv->clk[clk_i] = 1903a082ec9SNeil Zhang clk_get(&pdev->dev, pdata->clkname[clk_i]); 1913a082ec9SNeil Zhang if (IS_ERR(ehci_mv->clk[clk_i])) { 1923a082ec9SNeil Zhang dev_err(&pdev->dev, "error get clck \"%s\"\n", 1933a082ec9SNeil Zhang pdata->clkname[clk_i]); 1943a082ec9SNeil Zhang retval = PTR_ERR(ehci_mv->clk[clk_i]); 1953a082ec9SNeil Zhang goto err_put_clk; 1963a082ec9SNeil Zhang } 1973a082ec9SNeil Zhang } 1983a082ec9SNeil Zhang 1993a082ec9SNeil Zhang r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phyregs"); 2003a082ec9SNeil Zhang if (r == NULL) { 2013a082ec9SNeil Zhang dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); 2023a082ec9SNeil Zhang retval = -ENODEV; 2033a082ec9SNeil Zhang goto err_put_clk; 2043a082ec9SNeil Zhang } 2053a082ec9SNeil Zhang 2063a082ec9SNeil Zhang ehci_mv->phy_regs = ioremap(r->start, resource_size(r)); 2073a082ec9SNeil Zhang if (ehci_mv->phy_regs == 0) { 2083a082ec9SNeil Zhang dev_err(&pdev->dev, "failed to map phy I/O memory\n"); 2093a082ec9SNeil Zhang retval = -EFAULT; 2103a082ec9SNeil Zhang goto err_put_clk; 2113a082ec9SNeil Zhang } 2123a082ec9SNeil Zhang 2133a082ec9SNeil Zhang r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs"); 2143a082ec9SNeil Zhang if (!r) { 2153a082ec9SNeil Zhang dev_err(&pdev->dev, "no I/O memory resource defined\n"); 2163a082ec9SNeil Zhang retval = -ENODEV; 2173a082ec9SNeil Zhang goto err_iounmap_phyreg; 2183a082ec9SNeil Zhang } 2193a082ec9SNeil Zhang 2203a082ec9SNeil Zhang ehci_mv->cap_regs = ioremap(r->start, resource_size(r)); 2213a082ec9SNeil Zhang if (ehci_mv->cap_regs == NULL) { 2223a082ec9SNeil Zhang dev_err(&pdev->dev, "failed to map I/O memory\n"); 2233a082ec9SNeil Zhang retval = -EFAULT; 2243a082ec9SNeil Zhang goto err_iounmap_phyreg; 2253a082ec9SNeil Zhang } 2263a082ec9SNeil Zhang 2273a082ec9SNeil Zhang retval = mv_ehci_enable(ehci_mv); 2283a082ec9SNeil Zhang if (retval) { 2293a082ec9SNeil Zhang dev_err(&pdev->dev, "init phy error %d\n", retval); 2303a082ec9SNeil Zhang goto err_iounmap_capreg; 2313a082ec9SNeil Zhang } 2323a082ec9SNeil Zhang 2333a082ec9SNeil Zhang offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK; 2343a082ec9SNeil Zhang ehci_mv->op_regs = 2353a082ec9SNeil Zhang (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset); 2363a082ec9SNeil Zhang 2373a082ec9SNeil Zhang hcd->rsrc_start = r->start; 2383a082ec9SNeil Zhang hcd->rsrc_len = r->end - r->start + 1; 2393a082ec9SNeil Zhang hcd->regs = ehci_mv->op_regs; 2403a082ec9SNeil Zhang 2413a082ec9SNeil Zhang hcd->irq = platform_get_irq(pdev, 0); 2423a082ec9SNeil Zhang if (!hcd->irq) { 2433a082ec9SNeil Zhang dev_err(&pdev->dev, "Cannot get irq."); 2443a082ec9SNeil Zhang retval = -ENODEV; 2453a082ec9SNeil Zhang goto err_disable_clk; 2463a082ec9SNeil Zhang } 2473a082ec9SNeil Zhang 2483a082ec9SNeil Zhang ehci = hcd_to_ehci(hcd); 2493a082ec9SNeil Zhang ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs; 2503a082ec9SNeil Zhang ehci->regs = (struct ehci_regs *) ehci_mv->op_regs; 2513a082ec9SNeil Zhang ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); 2523a082ec9SNeil Zhang 2533a082ec9SNeil Zhang ehci_mv->mode = pdata->mode; 2543a082ec9SNeil Zhang if (ehci_mv->mode == MV_USB_MODE_OTG) { 2553a082ec9SNeil Zhang #ifdef CONFIG_USB_OTG_UTILS 2563a082ec9SNeil Zhang ehci_mv->otg = otg_get_transceiver(); 2573a082ec9SNeil Zhang if (!ehci_mv->otg) { 2583a082ec9SNeil Zhang dev_err(&pdev->dev, 2593a082ec9SNeil Zhang "unable to find transceiver\n"); 2603a082ec9SNeil Zhang retval = -ENODEV; 2613a082ec9SNeil Zhang goto err_disable_clk; 2623a082ec9SNeil Zhang } 2633a082ec9SNeil Zhang 2643a082ec9SNeil Zhang retval = otg_set_host(ehci_mv->otg, &hcd->self); 2653a082ec9SNeil Zhang if (retval < 0) { 2663a082ec9SNeil Zhang dev_err(&pdev->dev, 2673a082ec9SNeil Zhang "unable to register with transceiver\n"); 2683a082ec9SNeil Zhang retval = -ENODEV; 2693a082ec9SNeil Zhang goto err_put_transceiver; 2703a082ec9SNeil Zhang } 2713a082ec9SNeil Zhang /* otg will enable clock before use as host */ 2723a082ec9SNeil Zhang mv_ehci_disable(ehci_mv); 2733a082ec9SNeil Zhang #else 2743a082ec9SNeil Zhang dev_info(&pdev->dev, "MV_USB_MODE_OTG " 2753a082ec9SNeil Zhang "must have CONFIG_USB_OTG_UTILS enabled\n"); 2763a082ec9SNeil Zhang goto err_disable_clk; 2773a082ec9SNeil Zhang #endif 2783a082ec9SNeil Zhang } else { 2793a082ec9SNeil Zhang if (pdata->set_vbus) 2803a082ec9SNeil Zhang pdata->set_vbus(1); 2813a082ec9SNeil Zhang 2823a082ec9SNeil Zhang retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); 2833a082ec9SNeil Zhang if (retval) { 2843a082ec9SNeil Zhang dev_err(&pdev->dev, 2853a082ec9SNeil Zhang "failed to add hcd with err %d\n", retval); 2863a082ec9SNeil Zhang goto err_set_vbus; 2873a082ec9SNeil Zhang } 2883a082ec9SNeil Zhang } 2893a082ec9SNeil Zhang 2903a082ec9SNeil Zhang if (pdata->private_init) 2913a082ec9SNeil Zhang pdata->private_init(ehci_mv->op_regs, ehci_mv->phy_regs); 2923a082ec9SNeil Zhang 2933a082ec9SNeil Zhang dev_info(&pdev->dev, 2943a082ec9SNeil Zhang "successful find EHCI device with regs 0x%p irq %d" 2953a082ec9SNeil Zhang " working in %s mode\n", hcd->regs, hcd->irq, 2963a082ec9SNeil Zhang ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host"); 2973a082ec9SNeil Zhang 2983a082ec9SNeil Zhang return 0; 2993a082ec9SNeil Zhang 3003a082ec9SNeil Zhang err_set_vbus: 3013a082ec9SNeil Zhang if (pdata->set_vbus) 3023a082ec9SNeil Zhang pdata->set_vbus(0); 3033a082ec9SNeil Zhang #ifdef CONFIG_USB_OTG_UTILS 3043a082ec9SNeil Zhang err_put_transceiver: 3053a082ec9SNeil Zhang if (ehci_mv->otg) 3063a082ec9SNeil Zhang otg_put_transceiver(ehci_mv->otg); 3073a082ec9SNeil Zhang #endif 3083a082ec9SNeil Zhang err_disable_clk: 3093a082ec9SNeil Zhang mv_ehci_disable(ehci_mv); 3103a082ec9SNeil Zhang err_iounmap_capreg: 3113a082ec9SNeil Zhang iounmap(ehci_mv->cap_regs); 3123a082ec9SNeil Zhang err_iounmap_phyreg: 3133a082ec9SNeil Zhang iounmap(ehci_mv->phy_regs); 3143a082ec9SNeil Zhang err_put_clk: 3153a082ec9SNeil Zhang for (clk_i--; clk_i >= 0; clk_i--) 3163a082ec9SNeil Zhang clk_put(ehci_mv->clk[clk_i]); 3173a082ec9SNeil Zhang platform_set_drvdata(pdev, NULL); 3183a082ec9SNeil Zhang kfree(ehci_mv); 3193a082ec9SNeil Zhang err_put_hcd: 3203a082ec9SNeil Zhang usb_put_hcd(hcd); 3213a082ec9SNeil Zhang 3223a082ec9SNeil Zhang return retval; 3233a082ec9SNeil Zhang } 3243a082ec9SNeil Zhang 3253a082ec9SNeil Zhang static int mv_ehci_remove(struct platform_device *pdev) 3263a082ec9SNeil Zhang { 3273a082ec9SNeil Zhang struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 3283a082ec9SNeil Zhang struct usb_hcd *hcd = ehci_mv->hcd; 3293a082ec9SNeil Zhang int clk_i; 3303a082ec9SNeil Zhang 3313a082ec9SNeil Zhang if (hcd->rh_registered) 3323a082ec9SNeil Zhang usb_remove_hcd(hcd); 3333a082ec9SNeil Zhang 3343a082ec9SNeil Zhang if (ehci_mv->otg) { 3353a082ec9SNeil Zhang otg_set_host(ehci_mv->otg, NULL); 3363a082ec9SNeil Zhang otg_put_transceiver(ehci_mv->otg); 3373a082ec9SNeil Zhang } 3383a082ec9SNeil Zhang 3393a082ec9SNeil Zhang if (ehci_mv->mode == MV_USB_MODE_HOST) { 3403a082ec9SNeil Zhang if (ehci_mv->pdata->set_vbus) 3413a082ec9SNeil Zhang ehci_mv->pdata->set_vbus(0); 3423a082ec9SNeil Zhang 3433a082ec9SNeil Zhang mv_ehci_disable(ehci_mv); 3443a082ec9SNeil Zhang } 3453a082ec9SNeil Zhang 3463a082ec9SNeil Zhang iounmap(ehci_mv->cap_regs); 3473a082ec9SNeil Zhang iounmap(ehci_mv->phy_regs); 3483a082ec9SNeil Zhang 3493a082ec9SNeil Zhang for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) 3503a082ec9SNeil Zhang clk_put(ehci_mv->clk[clk_i]); 3513a082ec9SNeil Zhang 3523a082ec9SNeil Zhang platform_set_drvdata(pdev, NULL); 3533a082ec9SNeil Zhang 3543a082ec9SNeil Zhang kfree(ehci_mv); 3553a082ec9SNeil Zhang usb_put_hcd(hcd); 3563a082ec9SNeil Zhang 3573a082ec9SNeil Zhang return 0; 3583a082ec9SNeil Zhang } 3593a082ec9SNeil Zhang 3603a082ec9SNeil Zhang MODULE_ALIAS("mv-ehci"); 3613a082ec9SNeil Zhang 3623a082ec9SNeil Zhang static const struct platform_device_id ehci_id_table[] = { 3633a082ec9SNeil Zhang {"pxa-u2oehci", PXA_U2OEHCI}, 3643a082ec9SNeil Zhang {"pxa-sph", PXA_SPH}, 3653a082ec9SNeil Zhang {"mmp3-hsic", MMP3_HSIC}, 3663a082ec9SNeil Zhang {"mmp3-fsic", MMP3_FSIC}, 3673a082ec9SNeil Zhang {}, 3683a082ec9SNeil Zhang }; 3693a082ec9SNeil Zhang 3703a082ec9SNeil Zhang static void mv_ehci_shutdown(struct platform_device *pdev) 3713a082ec9SNeil Zhang { 3723a082ec9SNeil Zhang struct ehci_hcd_mv *ehci_mv = platform_get_drvdata(pdev); 3733a082ec9SNeil Zhang struct usb_hcd *hcd = ehci_mv->hcd; 3743a082ec9SNeil Zhang 3753a082ec9SNeil Zhang if (!hcd->rh_registered) 3763a082ec9SNeil Zhang return; 3773a082ec9SNeil Zhang 3783a082ec9SNeil Zhang if (hcd->driver->shutdown) 3793a082ec9SNeil Zhang hcd->driver->shutdown(hcd); 3803a082ec9SNeil Zhang } 3813a082ec9SNeil Zhang 3823a082ec9SNeil Zhang static struct platform_driver ehci_mv_driver = { 3833a082ec9SNeil Zhang .probe = mv_ehci_probe, 3843a082ec9SNeil Zhang .remove = mv_ehci_remove, 3853a082ec9SNeil Zhang .shutdown = mv_ehci_shutdown, 3863a082ec9SNeil Zhang .driver = { 3873a082ec9SNeil Zhang .name = "mv-ehci", 3883a082ec9SNeil Zhang .bus = &platform_bus_type, 3893a082ec9SNeil Zhang }, 3903a082ec9SNeil Zhang .id_table = ehci_id_table, 3913a082ec9SNeil Zhang }; 392