1efe7daf2SSergei Shtylyov /* 2efe7daf2SSergei Shtylyov * OHCI HCD (Host Controller Driver) for USB. 3efe7daf2SSergei Shtylyov * 4efe7daf2SSergei Shtylyov * TI DA8xx (OMAP-L1x) Bus Glue 5efe7daf2SSergei Shtylyov * 6efe7daf2SSergei Shtylyov * Derived from: ohci-omap.c and ohci-s3c2410.c 7efe7daf2SSergei Shtylyov * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> 8efe7daf2SSergei Shtylyov * 9efe7daf2SSergei Shtylyov * This file is licensed under the terms of the GNU General Public License 10efe7daf2SSergei Shtylyov * version 2. This program is licensed "as is" without any warranty of any 11efe7daf2SSergei Shtylyov * kind, whether express or implied. 12efe7daf2SSergei Shtylyov */ 13efe7daf2SSergei Shtylyov 146c21caa3SManjunath Goudar #include <linux/clk.h> 156c21caa3SManjunath Goudar #include <linux/io.h> 16efe7daf2SSergei Shtylyov #include <linux/interrupt.h> 17efe7daf2SSergei Shtylyov #include <linux/jiffies.h> 186c21caa3SManjunath Goudar #include <linux/kernel.h> 196c21caa3SManjunath Goudar #include <linux/module.h> 20efe7daf2SSergei Shtylyov #include <linux/platform_device.h> 216110c425SDavid Lechner #include <linux/phy/phy.h> 22ec2a0833SArnd Bergmann #include <linux/platform_data/usb-davinci.h> 236c21caa3SManjunath Goudar #include <linux/usb.h> 246c21caa3SManjunath Goudar #include <linux/usb/hcd.h> 256c21caa3SManjunath Goudar #include <asm/unaligned.h> 26efe7daf2SSergei Shtylyov 276c21caa3SManjunath Goudar #include "ohci.h" 286c21caa3SManjunath Goudar 296c21caa3SManjunath Goudar #define DRIVER_DESC "DA8XX" 30*eacae5d2SAxel Haslam #define DRV_NAME "ohci-da8xx" 316c21caa3SManjunath Goudar 326c21caa3SManjunath Goudar static struct hc_driver __read_mostly ohci_da8xx_hc_driver; 336c21caa3SManjunath Goudar 346c21caa3SManjunath Goudar static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, 356c21caa3SManjunath Goudar u16 wValue, u16 wIndex, char *buf, u16 wLength); 366c21caa3SManjunath Goudar static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); 37efe7daf2SSergei Shtylyov 38efe7daf2SSergei Shtylyov static struct clk *usb11_clk; 396110c425SDavid Lechner static struct phy *usb11_phy; 40efe7daf2SSergei Shtylyov 41efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */ 42efe7daf2SSergei Shtylyov static volatile u16 ocic_mask; 43efe7daf2SSergei Shtylyov 446110c425SDavid Lechner static int ohci_da8xx_enable(void) 45efe7daf2SSergei Shtylyov { 466110c425SDavid Lechner int ret; 47efe7daf2SSergei Shtylyov 486110c425SDavid Lechner ret = clk_prepare_enable(usb11_clk); 496110c425SDavid Lechner if (ret) 506110c425SDavid Lechner return ret; 51efe7daf2SSergei Shtylyov 526110c425SDavid Lechner ret = phy_init(usb11_phy); 536110c425SDavid Lechner if (ret) 546110c425SDavid Lechner goto err_phy_init; 55efe7daf2SSergei Shtylyov 566110c425SDavid Lechner ret = phy_power_on(usb11_phy); 576110c425SDavid Lechner if (ret) 586110c425SDavid Lechner goto err_phy_power_on; 59efe7daf2SSergei Shtylyov 606110c425SDavid Lechner return 0; 616110c425SDavid Lechner 626110c425SDavid Lechner err_phy_power_on: 636110c425SDavid Lechner phy_exit(usb11_phy); 646110c425SDavid Lechner err_phy_init: 656110c425SDavid Lechner clk_disable_unprepare(usb11_clk); 666110c425SDavid Lechner 676110c425SDavid Lechner return ret; 68efe7daf2SSergei Shtylyov } 69efe7daf2SSergei Shtylyov 706110c425SDavid Lechner static void ohci_da8xx_disable(void) 716110c425SDavid Lechner { 726110c425SDavid Lechner phy_power_off(usb11_phy); 736110c425SDavid Lechner phy_exit(usb11_phy); 746110c425SDavid Lechner clk_disable_unprepare(usb11_clk); 75efe7daf2SSergei Shtylyov } 76efe7daf2SSergei Shtylyov 77efe7daf2SSergei Shtylyov /* 78efe7daf2SSergei Shtylyov * Handle the port over-current indicator change. 79efe7daf2SSergei Shtylyov */ 80efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, 81efe7daf2SSergei Shtylyov unsigned port) 82efe7daf2SSergei Shtylyov { 83efe7daf2SSergei Shtylyov ocic_mask |= 1 << port; 84efe7daf2SSergei Shtylyov 85efe7daf2SSergei Shtylyov /* Once over-current is detected, the port needs to be powered down */ 86efe7daf2SSergei Shtylyov if (hub->get_oci(port) > 0) 87efe7daf2SSergei Shtylyov hub->set_power(port, 0); 88efe7daf2SSergei Shtylyov } 89efe7daf2SSergei Shtylyov 906c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd) 91efe7daf2SSergei Shtylyov { 92efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 93d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 94efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 95efe7daf2SSergei Shtylyov int result; 96efe7daf2SSergei Shtylyov u32 rh_a; 97efe7daf2SSergei Shtylyov 98efe7daf2SSergei Shtylyov dev_dbg(dev, "starting USB controller\n"); 99efe7daf2SSergei Shtylyov 1006110c425SDavid Lechner result = ohci_da8xx_enable(); 1016110c425SDavid Lechner if (result < 0) 1026110c425SDavid Lechner return result; 103efe7daf2SSergei Shtylyov 104efe7daf2SSergei Shtylyov /* 105efe7daf2SSergei Shtylyov * DA8xx only have 1 port connected to the pins but the HC root hub 106efe7daf2SSergei Shtylyov * register A reports 2 ports, thus we'll have to override it... 107efe7daf2SSergei Shtylyov */ 108efe7daf2SSergei Shtylyov ohci->num_ports = 1; 109efe7daf2SSergei Shtylyov 1106c21caa3SManjunath Goudar result = ohci_setup(hcd); 1116110c425SDavid Lechner if (result < 0) { 1126110c425SDavid Lechner ohci_da8xx_disable(); 113efe7daf2SSergei Shtylyov return result; 1146110c425SDavid Lechner } 115efe7daf2SSergei Shtylyov 116efe7daf2SSergei Shtylyov /* 117efe7daf2SSergei Shtylyov * Since we're providing a board-specific root hub port power control 118efe7daf2SSergei Shtylyov * and over-current reporting, we have to override the HC root hub A 119efe7daf2SSergei Shtylyov * register's default value, so that ohci_hub_control() could return 120efe7daf2SSergei Shtylyov * the correct hub descriptor... 121efe7daf2SSergei Shtylyov */ 122efe7daf2SSergei Shtylyov rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); 123efe7daf2SSergei Shtylyov if (hub->set_power) { 124efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NPS; 125efe7daf2SSergei Shtylyov rh_a |= RH_A_PSM; 126efe7daf2SSergei Shtylyov } 127efe7daf2SSergei Shtylyov if (hub->get_oci) { 128efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NOCP; 129efe7daf2SSergei Shtylyov rh_a |= RH_A_OCPM; 130efe7daf2SSergei Shtylyov } 131efe7daf2SSergei Shtylyov rh_a &= ~RH_A_POTPGT; 132efe7daf2SSergei Shtylyov rh_a |= hub->potpgt << 24; 133efe7daf2SSergei Shtylyov ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); 134efe7daf2SSergei Shtylyov 135efe7daf2SSergei Shtylyov return result; 136efe7daf2SSergei Shtylyov } 137efe7daf2SSergei Shtylyov 138efe7daf2SSergei Shtylyov /* 139efe7daf2SSergei Shtylyov * Update the status data from the hub with the over-current indicator change. 140efe7daf2SSergei Shtylyov */ 141efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) 142efe7daf2SSergei Shtylyov { 1436c21caa3SManjunath Goudar int length = orig_ohci_hub_status_data(hcd, buf); 144efe7daf2SSergei Shtylyov 145efe7daf2SSergei Shtylyov /* See if we have OCIC bit set on port 1 */ 146efe7daf2SSergei Shtylyov if (ocic_mask & (1 << 1)) { 147efe7daf2SSergei Shtylyov dev_dbg(hcd->self.controller, "over-current indicator change " 148efe7daf2SSergei Shtylyov "on port 1\n"); 149efe7daf2SSergei Shtylyov 150efe7daf2SSergei Shtylyov if (!length) 151efe7daf2SSergei Shtylyov length = 1; 152efe7daf2SSergei Shtylyov 153efe7daf2SSergei Shtylyov buf[0] |= 1 << 1; 154efe7daf2SSergei Shtylyov } 155efe7daf2SSergei Shtylyov return length; 156efe7daf2SSergei Shtylyov } 157efe7daf2SSergei Shtylyov 158efe7daf2SSergei Shtylyov /* 159efe7daf2SSergei Shtylyov * Look at the control requests to the root hub and see if we need to override. 160efe7daf2SSergei Shtylyov */ 161efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 162efe7daf2SSergei Shtylyov u16 wIndex, char *buf, u16 wLength) 163efe7daf2SSergei Shtylyov { 164efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 165d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 166efe7daf2SSergei Shtylyov int temp; 167efe7daf2SSergei Shtylyov 168efe7daf2SSergei Shtylyov switch (typeReq) { 169efe7daf2SSergei Shtylyov case GetPortStatus: 170efe7daf2SSergei Shtylyov /* Check the port number */ 171efe7daf2SSergei Shtylyov if (wIndex != 1) 172efe7daf2SSergei Shtylyov break; 173efe7daf2SSergei Shtylyov 174efe7daf2SSergei Shtylyov dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); 175efe7daf2SSergei Shtylyov 176efe7daf2SSergei Shtylyov temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); 177efe7daf2SSergei Shtylyov 178efe7daf2SSergei Shtylyov /* The port power status (PPS) bit defaults to 1 */ 179efe7daf2SSergei Shtylyov if (hub->get_power && hub->get_power(wIndex) == 0) 180efe7daf2SSergei Shtylyov temp &= ~RH_PS_PPS; 181efe7daf2SSergei Shtylyov 182efe7daf2SSergei Shtylyov /* The port over-current indicator (POCI) bit is always 0 */ 183efe7daf2SSergei Shtylyov if (hub->get_oci && hub->get_oci(wIndex) > 0) 184efe7daf2SSergei Shtylyov temp |= RH_PS_POCI; 185efe7daf2SSergei Shtylyov 186efe7daf2SSergei Shtylyov /* The over-current indicator change (OCIC) bit is 0 too */ 187efe7daf2SSergei Shtylyov if (ocic_mask & (1 << wIndex)) 188efe7daf2SSergei Shtylyov temp |= RH_PS_OCIC; 189efe7daf2SSergei Shtylyov 190efe7daf2SSergei Shtylyov put_unaligned(cpu_to_le32(temp), (__le32 *)buf); 191efe7daf2SSergei Shtylyov return 0; 192efe7daf2SSergei Shtylyov case SetPortFeature: 193efe7daf2SSergei Shtylyov temp = 1; 194efe7daf2SSergei Shtylyov goto check_port; 195efe7daf2SSergei Shtylyov case ClearPortFeature: 196efe7daf2SSergei Shtylyov temp = 0; 197efe7daf2SSergei Shtylyov 198efe7daf2SSergei Shtylyov check_port: 199efe7daf2SSergei Shtylyov /* Check the port number */ 200efe7daf2SSergei Shtylyov if (wIndex != 1) 201efe7daf2SSergei Shtylyov break; 202efe7daf2SSergei Shtylyov 203efe7daf2SSergei Shtylyov switch (wValue) { 204efe7daf2SSergei Shtylyov case USB_PORT_FEAT_POWER: 205efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 206efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, "POWER"); 207efe7daf2SSergei Shtylyov 208efe7daf2SSergei Shtylyov if (!hub->set_power) 209efe7daf2SSergei Shtylyov return -EPIPE; 210efe7daf2SSergei Shtylyov 211efe7daf2SSergei Shtylyov return hub->set_power(wIndex, temp) ? -EPIPE : 0; 212efe7daf2SSergei Shtylyov case USB_PORT_FEAT_C_OVER_CURRENT: 213efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 214efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, 215efe7daf2SSergei Shtylyov "C_OVER_CURRENT"); 216efe7daf2SSergei Shtylyov 217efe7daf2SSergei Shtylyov if (temp) 218efe7daf2SSergei Shtylyov ocic_mask |= 1 << wIndex; 219efe7daf2SSergei Shtylyov else 220efe7daf2SSergei Shtylyov ocic_mask &= ~(1 << wIndex); 221efe7daf2SSergei Shtylyov return 0; 222efe7daf2SSergei Shtylyov } 223efe7daf2SSergei Shtylyov } 224efe7daf2SSergei Shtylyov 2256c21caa3SManjunath Goudar return orig_ohci_hub_control(hcd, typeReq, wValue, 2266c21caa3SManjunath Goudar wIndex, buf, wLength); 227efe7daf2SSergei Shtylyov } 228efe7daf2SSergei Shtylyov 229efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/ 230efe7daf2SSergei Shtylyov 2316c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev) 232efe7daf2SSergei Shtylyov { 233d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); 234efe7daf2SSergei Shtylyov struct usb_hcd *hcd; 235efe7daf2SSergei Shtylyov struct resource *mem; 236efe7daf2SSergei Shtylyov int error, irq; 237efe7daf2SSergei Shtylyov 238efe7daf2SSergei Shtylyov if (hub == NULL) 239efe7daf2SSergei Shtylyov return -ENODEV; 240efe7daf2SSergei Shtylyov 241644db166SJingoo Han usb11_clk = devm_clk_get(&pdev->dev, "usb11"); 2426110c425SDavid Lechner if (IS_ERR(usb11_clk)) { 2436110c425SDavid Lechner if (PTR_ERR(usb11_clk) != -EPROBE_DEFER) 2446110c425SDavid Lechner dev_err(&pdev->dev, "Failed to get clock.\n"); 245efe7daf2SSergei Shtylyov return PTR_ERR(usb11_clk); 2466110c425SDavid Lechner } 247efe7daf2SSergei Shtylyov 2486110c425SDavid Lechner usb11_phy = devm_phy_get(&pdev->dev, "usb-phy"); 2496110c425SDavid Lechner if (IS_ERR(usb11_phy)) { 2506110c425SDavid Lechner if (PTR_ERR(usb11_phy) != -EPROBE_DEFER) 2516110c425SDavid Lechner dev_err(&pdev->dev, "Failed to get phy.\n"); 2526110c425SDavid Lechner return PTR_ERR(usb11_phy); 2536110c425SDavid Lechner } 254efe7daf2SSergei Shtylyov 2556c21caa3SManjunath Goudar hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev, 2566c21caa3SManjunath Goudar dev_name(&pdev->dev)); 257644db166SJingoo Han if (!hcd) 258644db166SJingoo Han return -ENOMEM; 259efe7daf2SSergei Shtylyov 260efe7daf2SSergei Shtylyov mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 261644db166SJingoo Han hcd->regs = devm_ioremap_resource(&pdev->dev, mem); 262644db166SJingoo Han if (IS_ERR(hcd->regs)) { 263644db166SJingoo Han error = PTR_ERR(hcd->regs); 264644db166SJingoo Han goto err; 265efe7daf2SSergei Shtylyov } 26654891d74SVarka Bhadram hcd->rsrc_start = mem->start; 26754891d74SVarka Bhadram hcd->rsrc_len = resource_size(mem); 268efe7daf2SSergei Shtylyov 269efe7daf2SSergei Shtylyov irq = platform_get_irq(pdev, 0); 270efe7daf2SSergei Shtylyov if (irq < 0) { 271efe7daf2SSergei Shtylyov error = -ENODEV; 272644db166SJingoo Han goto err; 273efe7daf2SSergei Shtylyov } 2746c21caa3SManjunath Goudar 275b5dd18d8SYong Zhang error = usb_add_hcd(hcd, irq, 0); 276efe7daf2SSergei Shtylyov if (error) 277644db166SJingoo Han goto err; 278efe7daf2SSergei Shtylyov 2793c9740a1SPeter Chen device_wakeup_enable(hcd->self.controller); 2803c9740a1SPeter Chen 281efe7daf2SSergei Shtylyov if (hub->ocic_notify) { 282efe7daf2SSergei Shtylyov error = hub->ocic_notify(ohci_da8xx_ocic_handler); 283efe7daf2SSergei Shtylyov if (!error) 284efe7daf2SSergei Shtylyov return 0; 285efe7daf2SSergei Shtylyov } 286efe7daf2SSergei Shtylyov 287efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 288644db166SJingoo Han err: 289efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 290efe7daf2SSergei Shtylyov return error; 291efe7daf2SSergei Shtylyov } 292efe7daf2SSergei Shtylyov 2936c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev) 294efe7daf2SSergei Shtylyov { 2956c21caa3SManjunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 296d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); 297efe7daf2SSergei Shtylyov 298efe7daf2SSergei Shtylyov hub->ocic_notify(NULL); 299efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 300efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 301efe7daf2SSergei Shtylyov 302efe7daf2SSergei Shtylyov return 0; 303efe7daf2SSergei Shtylyov } 304efe7daf2SSergei Shtylyov 305efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 306933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev, 307933bb1f0SMajunath Goudar pm_message_t message) 308efe7daf2SSergei Shtylyov { 309933bb1f0SMajunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 310efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 311933bb1f0SMajunath Goudar bool do_wakeup = device_may_wakeup(&pdev->dev); 312933bb1f0SMajunath Goudar int ret; 313933bb1f0SMajunath Goudar 314efe7daf2SSergei Shtylyov 315efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 316efe7daf2SSergei Shtylyov msleep(5); 317efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 318efe7daf2SSergei Shtylyov 319933bb1f0SMajunath Goudar ret = ohci_suspend(hcd, do_wakeup); 320933bb1f0SMajunath Goudar if (ret) 321933bb1f0SMajunath Goudar return ret; 322933bb1f0SMajunath Goudar 3236110c425SDavid Lechner ohci_da8xx_disable(); 324efe7daf2SSergei Shtylyov hcd->state = HC_STATE_SUSPENDED; 325933bb1f0SMajunath Goudar 326933bb1f0SMajunath Goudar return ret; 327efe7daf2SSergei Shtylyov } 328efe7daf2SSergei Shtylyov 329efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev) 330efe7daf2SSergei Shtylyov { 331efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 332efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 3336110c425SDavid Lechner int ret; 334efe7daf2SSergei Shtylyov 335efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 336efe7daf2SSergei Shtylyov msleep(5); 337efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 338efe7daf2SSergei Shtylyov 3396110c425SDavid Lechner ret = ohci_da8xx_enable(); 3406110c425SDavid Lechner if (ret) 3416110c425SDavid Lechner return ret; 3426110c425SDavid Lechner 343efe7daf2SSergei Shtylyov dev->dev.power.power_state = PMSG_ON; 344efe7daf2SSergei Shtylyov usb_hcd_resume_root_hub(hcd); 3456110c425SDavid Lechner 346efe7daf2SSergei Shtylyov return 0; 347efe7daf2SSergei Shtylyov } 348efe7daf2SSergei Shtylyov #endif 349efe7daf2SSergei Shtylyov 3506c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = { 3516c21caa3SManjunath Goudar .reset = ohci_da8xx_reset, 3526c21caa3SManjunath Goudar }; 3536c21caa3SManjunath Goudar 354efe7daf2SSergei Shtylyov /* 355efe7daf2SSergei Shtylyov * Driver definition to register with platform structure. 356efe7daf2SSergei Shtylyov */ 357efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = { 3586c21caa3SManjunath Goudar .probe = ohci_da8xx_probe, 3596c21caa3SManjunath Goudar .remove = ohci_da8xx_remove, 360efe7daf2SSergei Shtylyov .shutdown = usb_hcd_platform_shutdown, 361efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 362efe7daf2SSergei Shtylyov .suspend = ohci_da8xx_suspend, 363efe7daf2SSergei Shtylyov .resume = ohci_da8xx_resume, 364efe7daf2SSergei Shtylyov #endif 365efe7daf2SSergei Shtylyov .driver = { 3666c21caa3SManjunath Goudar .name = DRV_NAME, 367efe7daf2SSergei Shtylyov }, 368efe7daf2SSergei Shtylyov }; 369ab59ac01SJan Luebbe 3706c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void) 3716c21caa3SManjunath Goudar { 3726c21caa3SManjunath Goudar 3736c21caa3SManjunath Goudar if (usb_disabled()) 3746c21caa3SManjunath Goudar return -ENODEV; 3756c21caa3SManjunath Goudar 3766c21caa3SManjunath Goudar pr_info("%s: " DRIVER_DESC "\n", DRV_NAME); 3776c21caa3SManjunath Goudar ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides); 3786c21caa3SManjunath Goudar 3796c21caa3SManjunath Goudar /* 3806c21caa3SManjunath Goudar * The Davinci da8xx HW has some unusual quirks, which require 3816c21caa3SManjunath Goudar * da8xx-specific workarounds. We override certain hc_driver 3826c21caa3SManjunath Goudar * functions here to achieve that. We explicitly do not enhance 3836c21caa3SManjunath Goudar * ohci_driver_overrides to allow this more easily, since this 3846c21caa3SManjunath Goudar * is an unusual case, and we don't want to encourage others to 3856c21caa3SManjunath Goudar * override these functions by making it too easy. 3866c21caa3SManjunath Goudar */ 3876c21caa3SManjunath Goudar 3886c21caa3SManjunath Goudar orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control; 3896c21caa3SManjunath Goudar orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data; 3906c21caa3SManjunath Goudar 3916c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data; 3926c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control; 3936c21caa3SManjunath Goudar 3946c21caa3SManjunath Goudar return platform_driver_register(&ohci_hcd_da8xx_driver); 3956c21caa3SManjunath Goudar } 3966c21caa3SManjunath Goudar module_init(ohci_da8xx_init); 3976c21caa3SManjunath Goudar 3986c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void) 3996c21caa3SManjunath Goudar { 4006c21caa3SManjunath Goudar platform_driver_unregister(&ohci_hcd_da8xx_driver); 4016c21caa3SManjunath Goudar } 4026c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit); 4036c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC); 4046c21caa3SManjunath Goudar MODULE_LICENSE("GPL"); 4056c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME); 406