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" 30eacae5d2SAxel 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 38c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd { 39c7a4f9f3SAxel Haslam struct clk *usb11_clk; 40c7a4f9f3SAxel Haslam struct phy *usb11_phy; 41c7a4f9f3SAxel Haslam }; 42c7a4f9f3SAxel Haslam 43c7a4f9f3SAxel Haslam #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) 44efe7daf2SSergei Shtylyov 45efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */ 46efe7daf2SSergei Shtylyov static volatile u16 ocic_mask; 47efe7daf2SSergei Shtylyov 48c7a4f9f3SAxel Haslam static int ohci_da8xx_enable(struct usb_hcd *hcd) 49efe7daf2SSergei Shtylyov { 50c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 516110c425SDavid Lechner int ret; 52efe7daf2SSergei Shtylyov 53c7a4f9f3SAxel Haslam ret = clk_prepare_enable(da8xx_ohci->usb11_clk); 546110c425SDavid Lechner if (ret) 556110c425SDavid Lechner return ret; 56efe7daf2SSergei Shtylyov 57c7a4f9f3SAxel Haslam ret = phy_init(da8xx_ohci->usb11_phy); 586110c425SDavid Lechner if (ret) 596110c425SDavid Lechner goto err_phy_init; 60efe7daf2SSergei Shtylyov 61c7a4f9f3SAxel Haslam ret = phy_power_on(da8xx_ohci->usb11_phy); 626110c425SDavid Lechner if (ret) 636110c425SDavid Lechner goto err_phy_power_on; 64efe7daf2SSergei Shtylyov 656110c425SDavid Lechner return 0; 666110c425SDavid Lechner 676110c425SDavid Lechner err_phy_power_on: 68c7a4f9f3SAxel Haslam phy_exit(da8xx_ohci->usb11_phy); 696110c425SDavid Lechner err_phy_init: 70c7a4f9f3SAxel Haslam clk_disable_unprepare(da8xx_ohci->usb11_clk); 716110c425SDavid Lechner 726110c425SDavid Lechner return ret; 73efe7daf2SSergei Shtylyov } 74efe7daf2SSergei Shtylyov 75c7a4f9f3SAxel Haslam static void ohci_da8xx_disable(struct usb_hcd *hcd) 766110c425SDavid Lechner { 77c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); 78c7a4f9f3SAxel Haslam 79c7a4f9f3SAxel Haslam phy_power_off(da8xx_ohci->usb11_phy); 80c7a4f9f3SAxel Haslam phy_exit(da8xx_ohci->usb11_phy); 81c7a4f9f3SAxel Haslam clk_disable_unprepare(da8xx_ohci->usb11_clk); 82efe7daf2SSergei Shtylyov } 83efe7daf2SSergei Shtylyov 84*f3c56fb3SAxel Haslam static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) 85*f3c56fb3SAxel Haslam { 86*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 87*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 88*f3c56fb3SAxel Haslam 89*f3c56fb3SAxel Haslam if (hub && hub->set_power) 90*f3c56fb3SAxel Haslam return hub->set_power(1, on); 91*f3c56fb3SAxel Haslam 92*f3c56fb3SAxel Haslam return 0; 93*f3c56fb3SAxel Haslam } 94*f3c56fb3SAxel Haslam 95*f3c56fb3SAxel Haslam static int ohci_da8xx_get_power(struct usb_hcd *hcd) 96*f3c56fb3SAxel Haslam { 97*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 98*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 99*f3c56fb3SAxel Haslam 100*f3c56fb3SAxel Haslam if (hub && hub->get_power) 101*f3c56fb3SAxel Haslam return hub->get_power(1); 102*f3c56fb3SAxel Haslam 103*f3c56fb3SAxel Haslam return 1; 104*f3c56fb3SAxel Haslam } 105*f3c56fb3SAxel Haslam 106*f3c56fb3SAxel Haslam static int ohci_da8xx_get_oci(struct usb_hcd *hcd) 107*f3c56fb3SAxel Haslam { 108*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 109*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 110*f3c56fb3SAxel Haslam 111*f3c56fb3SAxel Haslam if (hub && hub->get_oci) 112*f3c56fb3SAxel Haslam return hub->get_oci(1); 113*f3c56fb3SAxel Haslam 114*f3c56fb3SAxel Haslam return 0; 115*f3c56fb3SAxel Haslam } 116*f3c56fb3SAxel Haslam 117*f3c56fb3SAxel Haslam static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) 118*f3c56fb3SAxel Haslam { 119*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 120*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 121*f3c56fb3SAxel Haslam 122*f3c56fb3SAxel Haslam if (hub && hub->set_power) 123*f3c56fb3SAxel Haslam return 1; 124*f3c56fb3SAxel Haslam 125*f3c56fb3SAxel Haslam return 0; 126*f3c56fb3SAxel Haslam } 127*f3c56fb3SAxel Haslam 128*f3c56fb3SAxel Haslam static int ohci_da8xx_has_oci(struct usb_hcd *hcd) 129*f3c56fb3SAxel Haslam { 130*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 131*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 132*f3c56fb3SAxel Haslam 133*f3c56fb3SAxel Haslam if (hub && hub->get_oci) 134*f3c56fb3SAxel Haslam return 1; 135*f3c56fb3SAxel Haslam 136*f3c56fb3SAxel Haslam return 0; 137*f3c56fb3SAxel Haslam } 138*f3c56fb3SAxel Haslam 139*f3c56fb3SAxel Haslam static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) 140*f3c56fb3SAxel Haslam { 141*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 142*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 143*f3c56fb3SAxel Haslam 144*f3c56fb3SAxel Haslam if (hub && hub->potpgt) 145*f3c56fb3SAxel Haslam return 1; 146*f3c56fb3SAxel Haslam 147*f3c56fb3SAxel Haslam return 0; 148*f3c56fb3SAxel Haslam } 149*f3c56fb3SAxel Haslam 150efe7daf2SSergei Shtylyov /* 151efe7daf2SSergei Shtylyov * Handle the port over-current indicator change. 152efe7daf2SSergei Shtylyov */ 153efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, 154efe7daf2SSergei Shtylyov unsigned port) 155efe7daf2SSergei Shtylyov { 156efe7daf2SSergei Shtylyov ocic_mask |= 1 << port; 157efe7daf2SSergei Shtylyov 158efe7daf2SSergei Shtylyov /* Once over-current is detected, the port needs to be powered down */ 159efe7daf2SSergei Shtylyov if (hub->get_oci(port) > 0) 160efe7daf2SSergei Shtylyov hub->set_power(port, 0); 161efe7daf2SSergei Shtylyov } 162efe7daf2SSergei Shtylyov 163*f3c56fb3SAxel Haslam static int ohci_da8xx_register_notify(struct usb_hcd *hcd) 164*f3c56fb3SAxel Haslam { 165*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 166*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 167*f3c56fb3SAxel Haslam 168*f3c56fb3SAxel Haslam if (hub && hub->ocic_notify) 169*f3c56fb3SAxel Haslam return hub->ocic_notify(ohci_da8xx_ocic_handler); 170*f3c56fb3SAxel Haslam 171*f3c56fb3SAxel Haslam return 0; 172*f3c56fb3SAxel Haslam } 173*f3c56fb3SAxel Haslam 174*f3c56fb3SAxel Haslam static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd) 175*f3c56fb3SAxel Haslam { 176*f3c56fb3SAxel Haslam struct device *dev = hcd->self.controller; 177*f3c56fb3SAxel Haslam struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 178*f3c56fb3SAxel Haslam 179*f3c56fb3SAxel Haslam if (hub && hub->ocic_notify) 180*f3c56fb3SAxel Haslam hub->ocic_notify(NULL); 181*f3c56fb3SAxel Haslam } 182*f3c56fb3SAxel Haslam 1836c21caa3SManjunath Goudar static int ohci_da8xx_reset(struct usb_hcd *hcd) 184efe7daf2SSergei Shtylyov { 185efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 186d4f09e28SJingoo Han struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); 187efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 188efe7daf2SSergei Shtylyov int result; 189efe7daf2SSergei Shtylyov u32 rh_a; 190efe7daf2SSergei Shtylyov 191efe7daf2SSergei Shtylyov dev_dbg(dev, "starting USB controller\n"); 192efe7daf2SSergei Shtylyov 193c7a4f9f3SAxel Haslam result = ohci_da8xx_enable(hcd); 1946110c425SDavid Lechner if (result < 0) 1956110c425SDavid Lechner return result; 196efe7daf2SSergei Shtylyov 197efe7daf2SSergei Shtylyov /* 198efe7daf2SSergei Shtylyov * DA8xx only have 1 port connected to the pins but the HC root hub 199efe7daf2SSergei Shtylyov * register A reports 2 ports, thus we'll have to override it... 200efe7daf2SSergei Shtylyov */ 201efe7daf2SSergei Shtylyov ohci->num_ports = 1; 202efe7daf2SSergei Shtylyov 2036c21caa3SManjunath Goudar result = ohci_setup(hcd); 2046110c425SDavid Lechner if (result < 0) { 205c7a4f9f3SAxel Haslam ohci_da8xx_disable(hcd); 206efe7daf2SSergei Shtylyov return result; 2076110c425SDavid Lechner } 208efe7daf2SSergei Shtylyov 209efe7daf2SSergei Shtylyov /* 210efe7daf2SSergei Shtylyov * Since we're providing a board-specific root hub port power control 211efe7daf2SSergei Shtylyov * and over-current reporting, we have to override the HC root hub A 212efe7daf2SSergei Shtylyov * register's default value, so that ohci_hub_control() could return 213efe7daf2SSergei Shtylyov * the correct hub descriptor... 214efe7daf2SSergei Shtylyov */ 215efe7daf2SSergei Shtylyov rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); 216*f3c56fb3SAxel Haslam if (ohci_da8xx_has_set_power(hcd)) { 217efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NPS; 218efe7daf2SSergei Shtylyov rh_a |= RH_A_PSM; 219efe7daf2SSergei Shtylyov } 220*f3c56fb3SAxel Haslam if (ohci_da8xx_has_oci(hcd)) { 221efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NOCP; 222efe7daf2SSergei Shtylyov rh_a |= RH_A_OCPM; 223efe7daf2SSergei Shtylyov } 224*f3c56fb3SAxel Haslam if (ohci_da8xx_has_potpgt(hcd)) { 225efe7daf2SSergei Shtylyov rh_a &= ~RH_A_POTPGT; 226efe7daf2SSergei Shtylyov rh_a |= hub->potpgt << 24; 227*f3c56fb3SAxel Haslam } 228efe7daf2SSergei Shtylyov ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); 229efe7daf2SSergei Shtylyov 230efe7daf2SSergei Shtylyov return result; 231efe7daf2SSergei Shtylyov } 232efe7daf2SSergei Shtylyov 233efe7daf2SSergei Shtylyov /* 234efe7daf2SSergei Shtylyov * Update the status data from the hub with the over-current indicator change. 235efe7daf2SSergei Shtylyov */ 236efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) 237efe7daf2SSergei Shtylyov { 2386c21caa3SManjunath Goudar int length = orig_ohci_hub_status_data(hcd, buf); 239efe7daf2SSergei Shtylyov 240efe7daf2SSergei Shtylyov /* See if we have OCIC bit set on port 1 */ 241efe7daf2SSergei Shtylyov if (ocic_mask & (1 << 1)) { 242efe7daf2SSergei Shtylyov dev_dbg(hcd->self.controller, "over-current indicator change " 243efe7daf2SSergei Shtylyov "on port 1\n"); 244efe7daf2SSergei Shtylyov 245efe7daf2SSergei Shtylyov if (!length) 246efe7daf2SSergei Shtylyov length = 1; 247efe7daf2SSergei Shtylyov 248efe7daf2SSergei Shtylyov buf[0] |= 1 << 1; 249efe7daf2SSergei Shtylyov } 250efe7daf2SSergei Shtylyov return length; 251efe7daf2SSergei Shtylyov } 252efe7daf2SSergei Shtylyov 253efe7daf2SSergei Shtylyov /* 254efe7daf2SSergei Shtylyov * Look at the control requests to the root hub and see if we need to override. 255efe7daf2SSergei Shtylyov */ 256efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 257efe7daf2SSergei Shtylyov u16 wIndex, char *buf, u16 wLength) 258efe7daf2SSergei Shtylyov { 259efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 260efe7daf2SSergei Shtylyov int temp; 261efe7daf2SSergei Shtylyov 262efe7daf2SSergei Shtylyov switch (typeReq) { 263efe7daf2SSergei Shtylyov case GetPortStatus: 264efe7daf2SSergei Shtylyov /* Check the port number */ 265efe7daf2SSergei Shtylyov if (wIndex != 1) 266efe7daf2SSergei Shtylyov break; 267efe7daf2SSergei Shtylyov 268efe7daf2SSergei Shtylyov dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); 269efe7daf2SSergei Shtylyov 270efe7daf2SSergei Shtylyov temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); 271efe7daf2SSergei Shtylyov 272efe7daf2SSergei Shtylyov /* The port power status (PPS) bit defaults to 1 */ 273*f3c56fb3SAxel Haslam if (!ohci_da8xx_get_power(hcd)) 274efe7daf2SSergei Shtylyov temp &= ~RH_PS_PPS; 275efe7daf2SSergei Shtylyov 276efe7daf2SSergei Shtylyov /* The port over-current indicator (POCI) bit is always 0 */ 277*f3c56fb3SAxel Haslam if (ohci_da8xx_get_oci(hcd) > 0) 278efe7daf2SSergei Shtylyov temp |= RH_PS_POCI; 279efe7daf2SSergei Shtylyov 280efe7daf2SSergei Shtylyov /* The over-current indicator change (OCIC) bit is 0 too */ 281efe7daf2SSergei Shtylyov if (ocic_mask & (1 << wIndex)) 282efe7daf2SSergei Shtylyov temp |= RH_PS_OCIC; 283efe7daf2SSergei Shtylyov 284efe7daf2SSergei Shtylyov put_unaligned(cpu_to_le32(temp), (__le32 *)buf); 285efe7daf2SSergei Shtylyov return 0; 286efe7daf2SSergei Shtylyov case SetPortFeature: 287efe7daf2SSergei Shtylyov temp = 1; 288efe7daf2SSergei Shtylyov goto check_port; 289efe7daf2SSergei Shtylyov case ClearPortFeature: 290efe7daf2SSergei Shtylyov temp = 0; 291efe7daf2SSergei Shtylyov 292efe7daf2SSergei Shtylyov check_port: 293efe7daf2SSergei Shtylyov /* Check the port number */ 294efe7daf2SSergei Shtylyov if (wIndex != 1) 295efe7daf2SSergei Shtylyov break; 296efe7daf2SSergei Shtylyov 297efe7daf2SSergei Shtylyov switch (wValue) { 298efe7daf2SSergei Shtylyov case USB_PORT_FEAT_POWER: 299efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 300efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, "POWER"); 301efe7daf2SSergei Shtylyov 302*f3c56fb3SAxel Haslam return ohci_da8xx_set_power(hcd, temp) ? -EPIPE : 0; 303efe7daf2SSergei Shtylyov case USB_PORT_FEAT_C_OVER_CURRENT: 304efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 305efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, 306efe7daf2SSergei Shtylyov "C_OVER_CURRENT"); 307efe7daf2SSergei Shtylyov 308efe7daf2SSergei Shtylyov if (temp) 309efe7daf2SSergei Shtylyov ocic_mask |= 1 << wIndex; 310efe7daf2SSergei Shtylyov else 311efe7daf2SSergei Shtylyov ocic_mask &= ~(1 << wIndex); 312efe7daf2SSergei Shtylyov return 0; 313efe7daf2SSergei Shtylyov } 314efe7daf2SSergei Shtylyov } 315efe7daf2SSergei Shtylyov 3166c21caa3SManjunath Goudar return orig_ohci_hub_control(hcd, typeReq, wValue, 3176c21caa3SManjunath Goudar wIndex, buf, wLength); 318efe7daf2SSergei Shtylyov } 319efe7daf2SSergei Shtylyov 320efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/ 321efe7daf2SSergei Shtylyov 3226c21caa3SManjunath Goudar static int ohci_da8xx_probe(struct platform_device *pdev) 323efe7daf2SSergei Shtylyov { 324c7a4f9f3SAxel Haslam struct da8xx_ohci_hcd *da8xx_ohci; 325efe7daf2SSergei Shtylyov struct usb_hcd *hcd; 326efe7daf2SSergei Shtylyov struct resource *mem; 327efe7daf2SSergei Shtylyov int error, irq; 3286c21caa3SManjunath Goudar hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev, 3296c21caa3SManjunath Goudar dev_name(&pdev->dev)); 330644db166SJingoo Han if (!hcd) 331644db166SJingoo Han return -ENOMEM; 332efe7daf2SSergei Shtylyov 333c7a4f9f3SAxel Haslam da8xx_ohci = to_da8xx_ohci(hcd); 334c7a4f9f3SAxel Haslam 335c7a4f9f3SAxel Haslam da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11"); 336c7a4f9f3SAxel Haslam if (IS_ERR(da8xx_ohci->usb11_clk)) { 337c7a4f9f3SAxel Haslam error = PTR_ERR(da8xx_ohci->usb11_clk); 338c7a4f9f3SAxel Haslam if (error != -EPROBE_DEFER) 339c7a4f9f3SAxel Haslam dev_err(&pdev->dev, "Failed to get clock.\n"); 340c7a4f9f3SAxel Haslam goto err; 341c7a4f9f3SAxel Haslam } 342c7a4f9f3SAxel Haslam 343c7a4f9f3SAxel Haslam da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy"); 344c7a4f9f3SAxel Haslam if (IS_ERR(da8xx_ohci->usb11_phy)) { 345c7a4f9f3SAxel Haslam error = PTR_ERR(da8xx_ohci->usb11_phy); 346c7a4f9f3SAxel Haslam if (error != -EPROBE_DEFER) 347c7a4f9f3SAxel Haslam dev_err(&pdev->dev, "Failed to get phy.\n"); 348c7a4f9f3SAxel Haslam goto err; 349c7a4f9f3SAxel Haslam } 350c7a4f9f3SAxel Haslam 351efe7daf2SSergei Shtylyov mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 352644db166SJingoo Han hcd->regs = devm_ioremap_resource(&pdev->dev, mem); 353644db166SJingoo Han if (IS_ERR(hcd->regs)) { 354644db166SJingoo Han error = PTR_ERR(hcd->regs); 355644db166SJingoo Han goto err; 356efe7daf2SSergei Shtylyov } 35754891d74SVarka Bhadram hcd->rsrc_start = mem->start; 35854891d74SVarka Bhadram hcd->rsrc_len = resource_size(mem); 359efe7daf2SSergei Shtylyov 360efe7daf2SSergei Shtylyov irq = platform_get_irq(pdev, 0); 361efe7daf2SSergei Shtylyov if (irq < 0) { 362efe7daf2SSergei Shtylyov error = -ENODEV; 363644db166SJingoo Han goto err; 364efe7daf2SSergei Shtylyov } 3656c21caa3SManjunath Goudar 366b5dd18d8SYong Zhang error = usb_add_hcd(hcd, irq, 0); 367efe7daf2SSergei Shtylyov if (error) 368644db166SJingoo Han goto err; 369efe7daf2SSergei Shtylyov 3703c9740a1SPeter Chen device_wakeup_enable(hcd->self.controller); 3713c9740a1SPeter Chen 372*f3c56fb3SAxel Haslam error = ohci_da8xx_register_notify(hcd); 373*f3c56fb3SAxel Haslam if (error) 374*f3c56fb3SAxel Haslam goto err_remove_hcd; 375efe7daf2SSergei Shtylyov 376*f3c56fb3SAxel Haslam return 0; 377*f3c56fb3SAxel Haslam 378*f3c56fb3SAxel Haslam err_remove_hcd: 379efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 380644db166SJingoo Han err: 381efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 382efe7daf2SSergei Shtylyov return error; 383efe7daf2SSergei Shtylyov } 384efe7daf2SSergei Shtylyov 3856c21caa3SManjunath Goudar static int ohci_da8xx_remove(struct platform_device *pdev) 386efe7daf2SSergei Shtylyov { 3876c21caa3SManjunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 388efe7daf2SSergei Shtylyov 389*f3c56fb3SAxel Haslam ohci_da8xx_unregister_notify(hcd); 390efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 391efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 392efe7daf2SSergei Shtylyov 393efe7daf2SSergei Shtylyov return 0; 394efe7daf2SSergei Shtylyov } 395efe7daf2SSergei Shtylyov 396efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 397933bb1f0SMajunath Goudar static int ohci_da8xx_suspend(struct platform_device *pdev, 398933bb1f0SMajunath Goudar pm_message_t message) 399efe7daf2SSergei Shtylyov { 400933bb1f0SMajunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev); 401efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 402933bb1f0SMajunath Goudar bool do_wakeup = device_may_wakeup(&pdev->dev); 403933bb1f0SMajunath Goudar int ret; 404933bb1f0SMajunath Goudar 405efe7daf2SSergei Shtylyov 406efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 407efe7daf2SSergei Shtylyov msleep(5); 408efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 409efe7daf2SSergei Shtylyov 410933bb1f0SMajunath Goudar ret = ohci_suspend(hcd, do_wakeup); 411933bb1f0SMajunath Goudar if (ret) 412933bb1f0SMajunath Goudar return ret; 413933bb1f0SMajunath Goudar 414c7a4f9f3SAxel Haslam ohci_da8xx_disable(hcd); 415efe7daf2SSergei Shtylyov hcd->state = HC_STATE_SUSPENDED; 416933bb1f0SMajunath Goudar 417933bb1f0SMajunath Goudar return ret; 418efe7daf2SSergei Shtylyov } 419efe7daf2SSergei Shtylyov 420efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev) 421efe7daf2SSergei Shtylyov { 422efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 423efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 4246110c425SDavid Lechner int ret; 425efe7daf2SSergei Shtylyov 426efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 427efe7daf2SSergei Shtylyov msleep(5); 428efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 429efe7daf2SSergei Shtylyov 430c7a4f9f3SAxel Haslam ret = ohci_da8xx_enable(hcd); 4316110c425SDavid Lechner if (ret) 4326110c425SDavid Lechner return ret; 4336110c425SDavid Lechner 434efe7daf2SSergei Shtylyov dev->dev.power.power_state = PMSG_ON; 435efe7daf2SSergei Shtylyov usb_hcd_resume_root_hub(hcd); 4366110c425SDavid Lechner 437efe7daf2SSergei Shtylyov return 0; 438efe7daf2SSergei Shtylyov } 439efe7daf2SSergei Shtylyov #endif 440efe7daf2SSergei Shtylyov 4416c21caa3SManjunath Goudar static const struct ohci_driver_overrides da8xx_overrides __initconst = { 4426c21caa3SManjunath Goudar .reset = ohci_da8xx_reset, 443c7a4f9f3SAxel Haslam .extra_priv_size = sizeof(struct da8xx_ohci_hcd), 4446c21caa3SManjunath Goudar }; 4456c21caa3SManjunath Goudar 446efe7daf2SSergei Shtylyov /* 447efe7daf2SSergei Shtylyov * Driver definition to register with platform structure. 448efe7daf2SSergei Shtylyov */ 449efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = { 4506c21caa3SManjunath Goudar .probe = ohci_da8xx_probe, 4516c21caa3SManjunath Goudar .remove = ohci_da8xx_remove, 452efe7daf2SSergei Shtylyov .shutdown = usb_hcd_platform_shutdown, 453efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 454efe7daf2SSergei Shtylyov .suspend = ohci_da8xx_suspend, 455efe7daf2SSergei Shtylyov .resume = ohci_da8xx_resume, 456efe7daf2SSergei Shtylyov #endif 457efe7daf2SSergei Shtylyov .driver = { 4586c21caa3SManjunath Goudar .name = DRV_NAME, 459efe7daf2SSergei Shtylyov }, 460efe7daf2SSergei Shtylyov }; 461ab59ac01SJan Luebbe 4626c21caa3SManjunath Goudar static int __init ohci_da8xx_init(void) 4636c21caa3SManjunath Goudar { 4646c21caa3SManjunath Goudar 4656c21caa3SManjunath Goudar if (usb_disabled()) 4666c21caa3SManjunath Goudar return -ENODEV; 4676c21caa3SManjunath Goudar 4686c21caa3SManjunath Goudar pr_info("%s: " DRIVER_DESC "\n", DRV_NAME); 4696c21caa3SManjunath Goudar ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides); 4706c21caa3SManjunath Goudar 4716c21caa3SManjunath Goudar /* 4726c21caa3SManjunath Goudar * The Davinci da8xx HW has some unusual quirks, which require 4736c21caa3SManjunath Goudar * da8xx-specific workarounds. We override certain hc_driver 4746c21caa3SManjunath Goudar * functions here to achieve that. We explicitly do not enhance 4756c21caa3SManjunath Goudar * ohci_driver_overrides to allow this more easily, since this 4766c21caa3SManjunath Goudar * is an unusual case, and we don't want to encourage others to 4776c21caa3SManjunath Goudar * override these functions by making it too easy. 4786c21caa3SManjunath Goudar */ 4796c21caa3SManjunath Goudar 4806c21caa3SManjunath Goudar orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control; 4816c21caa3SManjunath Goudar orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data; 4826c21caa3SManjunath Goudar 4836c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data; 4846c21caa3SManjunath Goudar ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control; 4856c21caa3SManjunath Goudar 4866c21caa3SManjunath Goudar return platform_driver_register(&ohci_hcd_da8xx_driver); 4876c21caa3SManjunath Goudar } 4886c21caa3SManjunath Goudar module_init(ohci_da8xx_init); 4896c21caa3SManjunath Goudar 4906c21caa3SManjunath Goudar static void __exit ohci_da8xx_exit(void) 4916c21caa3SManjunath Goudar { 4926c21caa3SManjunath Goudar platform_driver_unregister(&ohci_hcd_da8xx_driver); 4936c21caa3SManjunath Goudar } 4946c21caa3SManjunath Goudar module_exit(ohci_da8xx_exit); 4956c21caa3SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC); 4966c21caa3SManjunath Goudar MODULE_LICENSE("GPL"); 4976c21caa3SManjunath Goudar MODULE_ALIAS("platform:" DRV_NAME); 498