162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic platform ohci driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007 Michael Buesch <m@bues.ch> 662306a36Sopenharmony_ci * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de> 762306a36Sopenharmony_ci * Copyright 2014 Hans de Goede <hdegoede@redhat.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from the OCHI-SSB driver 1062306a36Sopenharmony_ci * Derived from the OHCI-PCI driver 1162306a36Sopenharmony_ci * Copyright 1999 Roman Weissgaerber 1262306a36Sopenharmony_ci * Copyright 2000-2002 David Brownell 1362306a36Sopenharmony_ci * Copyright 1999 Linus Torvalds 1462306a36Sopenharmony_ci * Copyright 1999 Gregory P. Smith 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/hrtimer.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/reset.h> 2862306a36Sopenharmony_ci#include <linux/usb/ohci_pdriver.h> 2962306a36Sopenharmony_ci#include <linux/usb.h> 3062306a36Sopenharmony_ci#include <linux/usb/hcd.h> 3162306a36Sopenharmony_ci#include <linux/usb/of.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "ohci.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_DESC "OHCI generic platform driver" 3662306a36Sopenharmony_ci#define OHCI_MAX_CLKS 4 3762306a36Sopenharmony_ci#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct ohci_platform_priv { 4062306a36Sopenharmony_ci struct clk *clks[OHCI_MAX_CLKS]; 4162306a36Sopenharmony_ci struct reset_control *resets; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int ohci_platform_power_on(struct platform_device *dev) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 4762306a36Sopenharmony_ci struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 4862306a36Sopenharmony_ci int clk, ret; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { 5162306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clks[clk]); 5262306a36Sopenharmony_ci if (ret) 5362306a36Sopenharmony_ci goto err_disable_clks; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cierr_disable_clks: 5962306a36Sopenharmony_ci while (--clk >= 0) 6062306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void ohci_platform_power_off(struct platform_device *dev) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 6862306a36Sopenharmony_ci struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 6962306a36Sopenharmony_ci int clk; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) 7262306a36Sopenharmony_ci if (priv->clks[clk]) 7362306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct hc_driver __read_mostly ohci_platform_hc_driver; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct ohci_driver_overrides platform_overrides __initconst = { 7962306a36Sopenharmony_ci .product_desc = "Generic Platform OHCI controller", 8062306a36Sopenharmony_ci .extra_priv_size = sizeof(struct ohci_platform_priv), 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct usb_ohci_pdata ohci_platform_defaults = { 8462306a36Sopenharmony_ci .power_on = ohci_platform_power_on, 8562306a36Sopenharmony_ci .power_suspend = ohci_platform_power_off, 8662306a36Sopenharmony_ci .power_off = ohci_platform_power_off, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int ohci_platform_probe(struct platform_device *dev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct usb_hcd *hcd; 9262306a36Sopenharmony_ci struct resource *res_mem; 9362306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); 9462306a36Sopenharmony_ci struct ohci_platform_priv *priv; 9562306a36Sopenharmony_ci struct ohci_hcd *ohci; 9662306a36Sopenharmony_ci int err, irq, clk = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (usb_disabled()) 9962306a36Sopenharmony_ci return -ENODEV; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * Use reasonable defaults so platforms don't have to provide these 10362306a36Sopenharmony_ci * with DT probing on ARM. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci if (!pdata) 10662306a36Sopenharmony_ci pdata = &ohci_platform_defaults; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); 10962306a36Sopenharmony_ci if (err) 11062306a36Sopenharmony_ci return err; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci irq = platform_get_irq(dev, 0); 11362306a36Sopenharmony_ci if (irq < 0) 11462306a36Sopenharmony_ci return irq; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, 11762306a36Sopenharmony_ci dev_name(&dev->dev)); 11862306a36Sopenharmony_ci if (!hcd) 11962306a36Sopenharmony_ci return -ENOMEM; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 12262306a36Sopenharmony_ci dev->dev.platform_data = pdata; 12362306a36Sopenharmony_ci priv = hcd_to_ohci_priv(hcd); 12462306a36Sopenharmony_ci ohci = hcd_to_ohci(hcd); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (pdata == &ohci_platform_defaults && dev->dev.of_node) { 12762306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) 12862306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_BE_MMIO; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) 13162306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_BE_DESC; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian")) 13462306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "no-big-frame-no")) 13762306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_FRAME_NO; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, 14062306a36Sopenharmony_ci "remote-wakeup-connected")) 14162306a36Sopenharmony_ci ohci->hc_control = OHCI_CTRL_RWC; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci of_property_read_u32(dev->dev.of_node, "num-ports", 14462306a36Sopenharmony_ci &ohci->num_ports); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { 14762306a36Sopenharmony_ci priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); 14862306a36Sopenharmony_ci if (IS_ERR(priv->clks[clk])) { 14962306a36Sopenharmony_ci err = PTR_ERR(priv->clks[clk]); 15062306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 15162306a36Sopenharmony_ci goto err_put_clks; 15262306a36Sopenharmony_ci priv->clks[clk] = NULL; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci priv->resets = devm_reset_control_array_get_optional_shared( 15862306a36Sopenharmony_ci &dev->dev); 15962306a36Sopenharmony_ci if (IS_ERR(priv->resets)) { 16062306a36Sopenharmony_ci err = PTR_ERR(priv->resets); 16162306a36Sopenharmony_ci goto err_put_clks; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci err = reset_control_deassert(priv->resets); 16562306a36Sopenharmony_ci if (err) 16662306a36Sopenharmony_ci goto err_put_clks; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (pdata->big_endian_desc) 17062306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_BE_DESC; 17162306a36Sopenharmony_ci if (pdata->big_endian_mmio) 17262306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_BE_MMIO; 17362306a36Sopenharmony_ci if (pdata->no_big_frame_no) 17462306a36Sopenharmony_ci ohci->flags |= OHCI_QUIRK_FRAME_NO; 17562306a36Sopenharmony_ci if (pdata->num_ports) 17662306a36Sopenharmony_ci ohci->num_ports = pdata->num_ports; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO 17962306a36Sopenharmony_ci if (ohci->flags & OHCI_QUIRK_BE_MMIO) { 18062306a36Sopenharmony_ci dev_err(&dev->dev, 18162306a36Sopenharmony_ci "Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n"); 18262306a36Sopenharmony_ci err = -EINVAL; 18362306a36Sopenharmony_ci goto err_reset; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC 18762306a36Sopenharmony_ci if (ohci->flags & OHCI_QUIRK_BE_DESC) { 18862306a36Sopenharmony_ci dev_err(&dev->dev, 18962306a36Sopenharmony_ci "Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n"); 19062306a36Sopenharmony_ci err = -EINVAL; 19162306a36Sopenharmony_ci goto err_reset; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci#endif 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pm_runtime_set_active(&dev->dev); 19662306a36Sopenharmony_ci pm_runtime_enable(&dev->dev); 19762306a36Sopenharmony_ci if (pdata->power_on) { 19862306a36Sopenharmony_ci err = pdata->power_on(dev); 19962306a36Sopenharmony_ci if (err < 0) 20062306a36Sopenharmony_ci goto err_reset; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); 20462306a36Sopenharmony_ci if (IS_ERR(hcd->regs)) { 20562306a36Sopenharmony_ci err = PTR_ERR(hcd->regs); 20662306a36Sopenharmony_ci goto err_power; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci hcd->rsrc_start = res_mem->start; 20962306a36Sopenharmony_ci hcd->rsrc_len = resource_size(res_mem); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci hcd->tpl_support = of_usb_host_tpl_support(dev->dev.of_node); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci err = usb_add_hcd(hcd, irq, IRQF_SHARED); 21462306a36Sopenharmony_ci if (err) 21562306a36Sopenharmony_ci goto err_power; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cierr_power: 22462306a36Sopenharmony_ci if (pdata->power_off) 22562306a36Sopenharmony_ci pdata->power_off(dev); 22662306a36Sopenharmony_cierr_reset: 22762306a36Sopenharmony_ci pm_runtime_disable(&dev->dev); 22862306a36Sopenharmony_ci reset_control_assert(priv->resets); 22962306a36Sopenharmony_cierr_put_clks: 23062306a36Sopenharmony_ci while (--clk >= 0) 23162306a36Sopenharmony_ci clk_put(priv->clks[clk]); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (pdata == &ohci_platform_defaults) 23462306a36Sopenharmony_ci dev->dev.platform_data = NULL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci usb_put_hcd(hcd); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void ohci_platform_remove(struct platform_device *dev) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 24462306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); 24562306a36Sopenharmony_ci struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 24662306a36Sopenharmony_ci int clk; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci pm_runtime_get_sync(&dev->dev); 24962306a36Sopenharmony_ci usb_remove_hcd(hcd); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (pdata->power_off) 25262306a36Sopenharmony_ci pdata->power_off(dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci reset_control_assert(priv->resets); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) 25762306a36Sopenharmony_ci clk_put(priv->clks[clk]); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci usb_put_hcd(hcd); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci pm_runtime_put_sync(&dev->dev); 26262306a36Sopenharmony_ci pm_runtime_disable(&dev->dev); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (pdata == &ohci_platform_defaults) 26562306a36Sopenharmony_ci dev->dev.platform_data = NULL; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 26962306a36Sopenharmony_cistatic int ohci_platform_suspend(struct device *dev) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 27262306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev->platform_data; 27362306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 27462306a36Sopenharmony_ci bool do_wakeup = device_may_wakeup(dev); 27562306a36Sopenharmony_ci int ret; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = ohci_suspend(hcd, do_wakeup); 27862306a36Sopenharmony_ci if (ret) 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (pdata->power_suspend) 28262306a36Sopenharmony_ci pdata->power_suspend(pdev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int ohci_platform_resume_common(struct device *dev, bool hibernated) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 29062306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev_get_platdata(dev); 29162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (pdata->power_on) { 29462306a36Sopenharmony_ci int err = pdata->power_on(pdev); 29562306a36Sopenharmony_ci if (err < 0) 29662306a36Sopenharmony_ci return err; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ohci_resume(hcd, hibernated); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pm_runtime_disable(dev); 30262306a36Sopenharmony_ci pm_runtime_set_active(dev); 30362306a36Sopenharmony_ci pm_runtime_enable(dev); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int ohci_platform_resume(struct device *dev) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci return ohci_platform_resume_common(dev, false); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int ohci_platform_restore(struct device *dev) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci return ohci_platform_resume_common(dev, true); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic const struct of_device_id ohci_platform_ids[] = { 32062306a36Sopenharmony_ci { .compatible = "generic-ohci", }, 32162306a36Sopenharmony_ci { .compatible = "cavium,octeon-6335-ohci", }, 32262306a36Sopenharmony_ci { .compatible = "ti,ohci-omap3", }, 32362306a36Sopenharmony_ci { } 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ohci_platform_ids); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct platform_device_id ohci_platform_table[] = { 32862306a36Sopenharmony_ci { "ohci-platform", 0 }, 32962306a36Sopenharmony_ci { } 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, ohci_platform_table); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 33462306a36Sopenharmony_cistatic const struct dev_pm_ops ohci_platform_pm_ops = { 33562306a36Sopenharmony_ci .suspend = ohci_platform_suspend, 33662306a36Sopenharmony_ci .resume = ohci_platform_resume, 33762306a36Sopenharmony_ci .freeze = ohci_platform_suspend, 33862306a36Sopenharmony_ci .thaw = ohci_platform_resume, 33962306a36Sopenharmony_ci .poweroff = ohci_platform_suspend, 34062306a36Sopenharmony_ci .restore = ohci_platform_restore, 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci#endif 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic struct platform_driver ohci_platform_driver = { 34562306a36Sopenharmony_ci .id_table = ohci_platform_table, 34662306a36Sopenharmony_ci .probe = ohci_platform_probe, 34762306a36Sopenharmony_ci .remove_new = ohci_platform_remove, 34862306a36Sopenharmony_ci .shutdown = usb_hcd_platform_shutdown, 34962306a36Sopenharmony_ci .driver = { 35062306a36Sopenharmony_ci .name = "ohci-platform", 35162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 35262306a36Sopenharmony_ci .pm = &ohci_platform_pm_ops, 35362306a36Sopenharmony_ci#endif 35462306a36Sopenharmony_ci .of_match_table = ohci_platform_ids, 35562306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int __init ohci_platform_init(void) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci if (usb_disabled()) 36262306a36Sopenharmony_ci return -ENODEV; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides); 36562306a36Sopenharmony_ci return platform_driver_register(&ohci_platform_driver); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_cimodule_init(ohci_platform_init); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void __exit ohci_platform_cleanup(void) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci platform_driver_unregister(&ohci_platform_driver); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_cimodule_exit(ohci_platform_cleanup); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 37662306a36Sopenharmony_ciMODULE_AUTHOR("Hauke Mehrtens"); 37762306a36Sopenharmony_ciMODULE_AUTHOR("Alan Stern"); 37862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 379