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