1*efe7daf2SSergei Shtylyov /* 2*efe7daf2SSergei Shtylyov * OHCI HCD (Host Controller Driver) for USB. 3*efe7daf2SSergei Shtylyov * 4*efe7daf2SSergei Shtylyov * TI DA8xx (OMAP-L1x) Bus Glue 5*efe7daf2SSergei Shtylyov * 6*efe7daf2SSergei Shtylyov * Derived from: ohci-omap.c and ohci-s3c2410.c 7*efe7daf2SSergei Shtylyov * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> 8*efe7daf2SSergei Shtylyov * 9*efe7daf2SSergei Shtylyov * This file is licensed under the terms of the GNU General Public License 10*efe7daf2SSergei Shtylyov * version 2. This program is licensed "as is" without any warranty of any 11*efe7daf2SSergei Shtylyov * kind, whether express or implied. 12*efe7daf2SSergei Shtylyov */ 13*efe7daf2SSergei Shtylyov 14*efe7daf2SSergei Shtylyov #include <linux/interrupt.h> 15*efe7daf2SSergei Shtylyov #include <linux/jiffies.h> 16*efe7daf2SSergei Shtylyov #include <linux/platform_device.h> 17*efe7daf2SSergei Shtylyov #include <linux/clk.h> 18*efe7daf2SSergei Shtylyov 19*efe7daf2SSergei Shtylyov #include <mach/da8xx.h> 20*efe7daf2SSergei Shtylyov #include <mach/usb.h> 21*efe7daf2SSergei Shtylyov 22*efe7daf2SSergei Shtylyov #ifndef CONFIG_ARCH_DAVINCI_DA8XX 23*efe7daf2SSergei Shtylyov #error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX." 24*efe7daf2SSergei Shtylyov #endif 25*efe7daf2SSergei Shtylyov 26*efe7daf2SSergei Shtylyov #define CFGCHIP2 DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG) 27*efe7daf2SSergei Shtylyov 28*efe7daf2SSergei Shtylyov static struct clk *usb11_clk; 29*efe7daf2SSergei Shtylyov static struct clk *usb20_clk; 30*efe7daf2SSergei Shtylyov 31*efe7daf2SSergei Shtylyov /* Over-current indicator change bitmask */ 32*efe7daf2SSergei Shtylyov static volatile u16 ocic_mask; 33*efe7daf2SSergei Shtylyov 34*efe7daf2SSergei Shtylyov static void ohci_da8xx_clock(int on) 35*efe7daf2SSergei Shtylyov { 36*efe7daf2SSergei Shtylyov u32 cfgchip2; 37*efe7daf2SSergei Shtylyov 38*efe7daf2SSergei Shtylyov cfgchip2 = __raw_readl(CFGCHIP2); 39*efe7daf2SSergei Shtylyov if (on) { 40*efe7daf2SSergei Shtylyov clk_enable(usb11_clk); 41*efe7daf2SSergei Shtylyov 42*efe7daf2SSergei Shtylyov /* 43*efe7daf2SSergei Shtylyov * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we 44*efe7daf2SSergei Shtylyov * need to enable the USB 2.0 module clocking, start its PHY, 45*efe7daf2SSergei Shtylyov * and not allow it to stop the clock during USB 2.0 suspend. 46*efe7daf2SSergei Shtylyov */ 47*efe7daf2SSergei Shtylyov if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) { 48*efe7daf2SSergei Shtylyov clk_enable(usb20_clk); 49*efe7daf2SSergei Shtylyov 50*efe7daf2SSergei Shtylyov cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN); 51*efe7daf2SSergei Shtylyov cfgchip2 |= CFGCHIP2_PHY_PLLON; 52*efe7daf2SSergei Shtylyov __raw_writel(cfgchip2, CFGCHIP2); 53*efe7daf2SSergei Shtylyov 54*efe7daf2SSergei Shtylyov pr_info("Waiting for USB PHY clock good...\n"); 55*efe7daf2SSergei Shtylyov while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD)) 56*efe7daf2SSergei Shtylyov cpu_relax(); 57*efe7daf2SSergei Shtylyov } 58*efe7daf2SSergei Shtylyov 59*efe7daf2SSergei Shtylyov /* Enable USB 1.1 PHY */ 60*efe7daf2SSergei Shtylyov cfgchip2 |= CFGCHIP2_USB1SUSPENDM; 61*efe7daf2SSergei Shtylyov } else { 62*efe7daf2SSergei Shtylyov clk_disable(usb11_clk); 63*efe7daf2SSergei Shtylyov if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) 64*efe7daf2SSergei Shtylyov clk_disable(usb20_clk); 65*efe7daf2SSergei Shtylyov 66*efe7daf2SSergei Shtylyov /* Disable USB 1.1 PHY */ 67*efe7daf2SSergei Shtylyov cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM; 68*efe7daf2SSergei Shtylyov } 69*efe7daf2SSergei Shtylyov __raw_writel(cfgchip2, CFGCHIP2); 70*efe7daf2SSergei Shtylyov } 71*efe7daf2SSergei Shtylyov 72*efe7daf2SSergei Shtylyov /* 73*efe7daf2SSergei Shtylyov * Handle the port over-current indicator change. 74*efe7daf2SSergei Shtylyov */ 75*efe7daf2SSergei Shtylyov static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, 76*efe7daf2SSergei Shtylyov unsigned port) 77*efe7daf2SSergei Shtylyov { 78*efe7daf2SSergei Shtylyov ocic_mask |= 1 << port; 79*efe7daf2SSergei Shtylyov 80*efe7daf2SSergei Shtylyov /* Once over-current is detected, the port needs to be powered down */ 81*efe7daf2SSergei Shtylyov if (hub->get_oci(port) > 0) 82*efe7daf2SSergei Shtylyov hub->set_power(port, 0); 83*efe7daf2SSergei Shtylyov } 84*efe7daf2SSergei Shtylyov 85*efe7daf2SSergei Shtylyov static int ohci_da8xx_init(struct usb_hcd *hcd) 86*efe7daf2SSergei Shtylyov { 87*efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 88*efe7daf2SSergei Shtylyov struct da8xx_ohci_root_hub *hub = dev->platform_data; 89*efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 90*efe7daf2SSergei Shtylyov int result; 91*efe7daf2SSergei Shtylyov u32 rh_a; 92*efe7daf2SSergei Shtylyov 93*efe7daf2SSergei Shtylyov dev_dbg(dev, "starting USB controller\n"); 94*efe7daf2SSergei Shtylyov 95*efe7daf2SSergei Shtylyov ohci_da8xx_clock(1); 96*efe7daf2SSergei Shtylyov 97*efe7daf2SSergei Shtylyov /* 98*efe7daf2SSergei Shtylyov * DA8xx only have 1 port connected to the pins but the HC root hub 99*efe7daf2SSergei Shtylyov * register A reports 2 ports, thus we'll have to override it... 100*efe7daf2SSergei Shtylyov */ 101*efe7daf2SSergei Shtylyov ohci->num_ports = 1; 102*efe7daf2SSergei Shtylyov 103*efe7daf2SSergei Shtylyov result = ohci_init(ohci); 104*efe7daf2SSergei Shtylyov if (result < 0) 105*efe7daf2SSergei Shtylyov return result; 106*efe7daf2SSergei Shtylyov 107*efe7daf2SSergei Shtylyov /* 108*efe7daf2SSergei Shtylyov * Since we're providing a board-specific root hub port power control 109*efe7daf2SSergei Shtylyov * and over-current reporting, we have to override the HC root hub A 110*efe7daf2SSergei Shtylyov * register's default value, so that ohci_hub_control() could return 111*efe7daf2SSergei Shtylyov * the correct hub descriptor... 112*efe7daf2SSergei Shtylyov */ 113*efe7daf2SSergei Shtylyov rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); 114*efe7daf2SSergei Shtylyov if (hub->set_power) { 115*efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NPS; 116*efe7daf2SSergei Shtylyov rh_a |= RH_A_PSM; 117*efe7daf2SSergei Shtylyov } 118*efe7daf2SSergei Shtylyov if (hub->get_oci) { 119*efe7daf2SSergei Shtylyov rh_a &= ~RH_A_NOCP; 120*efe7daf2SSergei Shtylyov rh_a |= RH_A_OCPM; 121*efe7daf2SSergei Shtylyov } 122*efe7daf2SSergei Shtylyov rh_a &= ~RH_A_POTPGT; 123*efe7daf2SSergei Shtylyov rh_a |= hub->potpgt << 24; 124*efe7daf2SSergei Shtylyov ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); 125*efe7daf2SSergei Shtylyov 126*efe7daf2SSergei Shtylyov return result; 127*efe7daf2SSergei Shtylyov } 128*efe7daf2SSergei Shtylyov 129*efe7daf2SSergei Shtylyov static void ohci_da8xx_stop(struct usb_hcd *hcd) 130*efe7daf2SSergei Shtylyov { 131*efe7daf2SSergei Shtylyov ohci_stop(hcd); 132*efe7daf2SSergei Shtylyov ohci_da8xx_clock(0); 133*efe7daf2SSergei Shtylyov } 134*efe7daf2SSergei Shtylyov 135*efe7daf2SSergei Shtylyov static int ohci_da8xx_start(struct usb_hcd *hcd) 136*efe7daf2SSergei Shtylyov { 137*efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 138*efe7daf2SSergei Shtylyov int result; 139*efe7daf2SSergei Shtylyov 140*efe7daf2SSergei Shtylyov result = ohci_run(ohci); 141*efe7daf2SSergei Shtylyov if (result < 0) 142*efe7daf2SSergei Shtylyov ohci_da8xx_stop(hcd); 143*efe7daf2SSergei Shtylyov 144*efe7daf2SSergei Shtylyov return result; 145*efe7daf2SSergei Shtylyov } 146*efe7daf2SSergei Shtylyov 147*efe7daf2SSergei Shtylyov /* 148*efe7daf2SSergei Shtylyov * Update the status data from the hub with the over-current indicator change. 149*efe7daf2SSergei Shtylyov */ 150*efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) 151*efe7daf2SSergei Shtylyov { 152*efe7daf2SSergei Shtylyov int length = ohci_hub_status_data(hcd, buf); 153*efe7daf2SSergei Shtylyov 154*efe7daf2SSergei Shtylyov /* See if we have OCIC bit set on port 1 */ 155*efe7daf2SSergei Shtylyov if (ocic_mask & (1 << 1)) { 156*efe7daf2SSergei Shtylyov dev_dbg(hcd->self.controller, "over-current indicator change " 157*efe7daf2SSergei Shtylyov "on port 1\n"); 158*efe7daf2SSergei Shtylyov 159*efe7daf2SSergei Shtylyov if (!length) 160*efe7daf2SSergei Shtylyov length = 1; 161*efe7daf2SSergei Shtylyov 162*efe7daf2SSergei Shtylyov buf[0] |= 1 << 1; 163*efe7daf2SSergei Shtylyov } 164*efe7daf2SSergei Shtylyov return length; 165*efe7daf2SSergei Shtylyov } 166*efe7daf2SSergei Shtylyov 167*efe7daf2SSergei Shtylyov /* 168*efe7daf2SSergei Shtylyov * Look at the control requests to the root hub and see if we need to override. 169*efe7daf2SSergei Shtylyov */ 170*efe7daf2SSergei Shtylyov static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 171*efe7daf2SSergei Shtylyov u16 wIndex, char *buf, u16 wLength) 172*efe7daf2SSergei Shtylyov { 173*efe7daf2SSergei Shtylyov struct device *dev = hcd->self.controller; 174*efe7daf2SSergei Shtylyov struct da8xx_ohci_root_hub *hub = dev->platform_data; 175*efe7daf2SSergei Shtylyov int temp; 176*efe7daf2SSergei Shtylyov 177*efe7daf2SSergei Shtylyov switch (typeReq) { 178*efe7daf2SSergei Shtylyov case GetPortStatus: 179*efe7daf2SSergei Shtylyov /* Check the port number */ 180*efe7daf2SSergei Shtylyov if (wIndex != 1) 181*efe7daf2SSergei Shtylyov break; 182*efe7daf2SSergei Shtylyov 183*efe7daf2SSergei Shtylyov dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); 184*efe7daf2SSergei Shtylyov 185*efe7daf2SSergei Shtylyov temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); 186*efe7daf2SSergei Shtylyov 187*efe7daf2SSergei Shtylyov /* The port power status (PPS) bit defaults to 1 */ 188*efe7daf2SSergei Shtylyov if (hub->get_power && hub->get_power(wIndex) == 0) 189*efe7daf2SSergei Shtylyov temp &= ~RH_PS_PPS; 190*efe7daf2SSergei Shtylyov 191*efe7daf2SSergei Shtylyov /* The port over-current indicator (POCI) bit is always 0 */ 192*efe7daf2SSergei Shtylyov if (hub->get_oci && hub->get_oci(wIndex) > 0) 193*efe7daf2SSergei Shtylyov temp |= RH_PS_POCI; 194*efe7daf2SSergei Shtylyov 195*efe7daf2SSergei Shtylyov /* The over-current indicator change (OCIC) bit is 0 too */ 196*efe7daf2SSergei Shtylyov if (ocic_mask & (1 << wIndex)) 197*efe7daf2SSergei Shtylyov temp |= RH_PS_OCIC; 198*efe7daf2SSergei Shtylyov 199*efe7daf2SSergei Shtylyov put_unaligned(cpu_to_le32(temp), (__le32 *)buf); 200*efe7daf2SSergei Shtylyov return 0; 201*efe7daf2SSergei Shtylyov case SetPortFeature: 202*efe7daf2SSergei Shtylyov temp = 1; 203*efe7daf2SSergei Shtylyov goto check_port; 204*efe7daf2SSergei Shtylyov case ClearPortFeature: 205*efe7daf2SSergei Shtylyov temp = 0; 206*efe7daf2SSergei Shtylyov 207*efe7daf2SSergei Shtylyov check_port: 208*efe7daf2SSergei Shtylyov /* Check the port number */ 209*efe7daf2SSergei Shtylyov if (wIndex != 1) 210*efe7daf2SSergei Shtylyov break; 211*efe7daf2SSergei Shtylyov 212*efe7daf2SSergei Shtylyov switch (wValue) { 213*efe7daf2SSergei Shtylyov case USB_PORT_FEAT_POWER: 214*efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 215*efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, "POWER"); 216*efe7daf2SSergei Shtylyov 217*efe7daf2SSergei Shtylyov if (!hub->set_power) 218*efe7daf2SSergei Shtylyov return -EPIPE; 219*efe7daf2SSergei Shtylyov 220*efe7daf2SSergei Shtylyov return hub->set_power(wIndex, temp) ? -EPIPE : 0; 221*efe7daf2SSergei Shtylyov case USB_PORT_FEAT_C_OVER_CURRENT: 222*efe7daf2SSergei Shtylyov dev_dbg(dev, "%sPortFeature(%u): %s\n", 223*efe7daf2SSergei Shtylyov temp ? "Set" : "Clear", wIndex, 224*efe7daf2SSergei Shtylyov "C_OVER_CURRENT"); 225*efe7daf2SSergei Shtylyov 226*efe7daf2SSergei Shtylyov if (temp) 227*efe7daf2SSergei Shtylyov ocic_mask |= 1 << wIndex; 228*efe7daf2SSergei Shtylyov else 229*efe7daf2SSergei Shtylyov ocic_mask &= ~(1 << wIndex); 230*efe7daf2SSergei Shtylyov return 0; 231*efe7daf2SSergei Shtylyov } 232*efe7daf2SSergei Shtylyov } 233*efe7daf2SSergei Shtylyov 234*efe7daf2SSergei Shtylyov return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); 235*efe7daf2SSergei Shtylyov } 236*efe7daf2SSergei Shtylyov 237*efe7daf2SSergei Shtylyov static const struct hc_driver ohci_da8xx_hc_driver = { 238*efe7daf2SSergei Shtylyov .description = hcd_name, 239*efe7daf2SSergei Shtylyov .product_desc = "DA8xx OHCI", 240*efe7daf2SSergei Shtylyov .hcd_priv_size = sizeof(struct ohci_hcd), 241*efe7daf2SSergei Shtylyov 242*efe7daf2SSergei Shtylyov /* 243*efe7daf2SSergei Shtylyov * generic hardware linkage 244*efe7daf2SSergei Shtylyov */ 245*efe7daf2SSergei Shtylyov .irq = ohci_irq, 246*efe7daf2SSergei Shtylyov .flags = HCD_USB11 | HCD_MEMORY, 247*efe7daf2SSergei Shtylyov 248*efe7daf2SSergei Shtylyov /* 249*efe7daf2SSergei Shtylyov * basic lifecycle operations 250*efe7daf2SSergei Shtylyov */ 251*efe7daf2SSergei Shtylyov .reset = ohci_da8xx_init, 252*efe7daf2SSergei Shtylyov .start = ohci_da8xx_start, 253*efe7daf2SSergei Shtylyov .stop = ohci_da8xx_stop, 254*efe7daf2SSergei Shtylyov .shutdown = ohci_shutdown, 255*efe7daf2SSergei Shtylyov 256*efe7daf2SSergei Shtylyov /* 257*efe7daf2SSergei Shtylyov * managing i/o requests and associated device resources 258*efe7daf2SSergei Shtylyov */ 259*efe7daf2SSergei Shtylyov .urb_enqueue = ohci_urb_enqueue, 260*efe7daf2SSergei Shtylyov .urb_dequeue = ohci_urb_dequeue, 261*efe7daf2SSergei Shtylyov .endpoint_disable = ohci_endpoint_disable, 262*efe7daf2SSergei Shtylyov 263*efe7daf2SSergei Shtylyov /* 264*efe7daf2SSergei Shtylyov * scheduling support 265*efe7daf2SSergei Shtylyov */ 266*efe7daf2SSergei Shtylyov .get_frame_number = ohci_get_frame, 267*efe7daf2SSergei Shtylyov 268*efe7daf2SSergei Shtylyov /* 269*efe7daf2SSergei Shtylyov * root hub support 270*efe7daf2SSergei Shtylyov */ 271*efe7daf2SSergei Shtylyov .hub_status_data = ohci_da8xx_hub_status_data, 272*efe7daf2SSergei Shtylyov .hub_control = ohci_da8xx_hub_control, 273*efe7daf2SSergei Shtylyov 274*efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 275*efe7daf2SSergei Shtylyov .bus_suspend = ohci_bus_suspend, 276*efe7daf2SSergei Shtylyov .bus_resume = ohci_bus_resume, 277*efe7daf2SSergei Shtylyov #endif 278*efe7daf2SSergei Shtylyov .start_port_reset = ohci_start_port_reset, 279*efe7daf2SSergei Shtylyov }; 280*efe7daf2SSergei Shtylyov 281*efe7daf2SSergei Shtylyov /*-------------------------------------------------------------------------*/ 282*efe7daf2SSergei Shtylyov 283*efe7daf2SSergei Shtylyov 284*efe7daf2SSergei Shtylyov /** 285*efe7daf2SSergei Shtylyov * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs 286*efe7daf2SSergei Shtylyov * Context: !in_interrupt() 287*efe7daf2SSergei Shtylyov * 288*efe7daf2SSergei Shtylyov * Allocates basic resources for this USB host controller, and 289*efe7daf2SSergei Shtylyov * then invokes the start() method for the HCD associated with it 290*efe7daf2SSergei Shtylyov * through the hotplug entry's driver_data. 291*efe7daf2SSergei Shtylyov */ 292*efe7daf2SSergei Shtylyov static int usb_hcd_da8xx_probe(const struct hc_driver *driver, 293*efe7daf2SSergei Shtylyov struct platform_device *pdev) 294*efe7daf2SSergei Shtylyov { 295*efe7daf2SSergei Shtylyov struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; 296*efe7daf2SSergei Shtylyov struct usb_hcd *hcd; 297*efe7daf2SSergei Shtylyov struct resource *mem; 298*efe7daf2SSergei Shtylyov int error, irq; 299*efe7daf2SSergei Shtylyov 300*efe7daf2SSergei Shtylyov if (hub == NULL) 301*efe7daf2SSergei Shtylyov return -ENODEV; 302*efe7daf2SSergei Shtylyov 303*efe7daf2SSergei Shtylyov usb11_clk = clk_get(&pdev->dev, "usb11"); 304*efe7daf2SSergei Shtylyov if (IS_ERR(usb11_clk)) 305*efe7daf2SSergei Shtylyov return PTR_ERR(usb11_clk); 306*efe7daf2SSergei Shtylyov 307*efe7daf2SSergei Shtylyov usb20_clk = clk_get(&pdev->dev, "usb20"); 308*efe7daf2SSergei Shtylyov if (IS_ERR(usb20_clk)) { 309*efe7daf2SSergei Shtylyov error = PTR_ERR(usb20_clk); 310*efe7daf2SSergei Shtylyov goto err0; 311*efe7daf2SSergei Shtylyov } 312*efe7daf2SSergei Shtylyov 313*efe7daf2SSergei Shtylyov hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 314*efe7daf2SSergei Shtylyov if (!hcd) { 315*efe7daf2SSergei Shtylyov error = -ENOMEM; 316*efe7daf2SSergei Shtylyov goto err1; 317*efe7daf2SSergei Shtylyov } 318*efe7daf2SSergei Shtylyov 319*efe7daf2SSergei Shtylyov mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 320*efe7daf2SSergei Shtylyov if (!mem) { 321*efe7daf2SSergei Shtylyov error = -ENODEV; 322*efe7daf2SSergei Shtylyov goto err2; 323*efe7daf2SSergei Shtylyov } 324*efe7daf2SSergei Shtylyov hcd->rsrc_start = mem->start; 325*efe7daf2SSergei Shtylyov hcd->rsrc_len = mem->end - mem->start + 1; 326*efe7daf2SSergei Shtylyov 327*efe7daf2SSergei Shtylyov if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 328*efe7daf2SSergei Shtylyov dev_dbg(&pdev->dev, "request_mem_region failed\n"); 329*efe7daf2SSergei Shtylyov error = -EBUSY; 330*efe7daf2SSergei Shtylyov goto err2; 331*efe7daf2SSergei Shtylyov } 332*efe7daf2SSergei Shtylyov 333*efe7daf2SSergei Shtylyov hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 334*efe7daf2SSergei Shtylyov if (!hcd->regs) { 335*efe7daf2SSergei Shtylyov dev_err(&pdev->dev, "ioremap failed\n"); 336*efe7daf2SSergei Shtylyov error = -ENOMEM; 337*efe7daf2SSergei Shtylyov goto err3; 338*efe7daf2SSergei Shtylyov } 339*efe7daf2SSergei Shtylyov 340*efe7daf2SSergei Shtylyov ohci_hcd_init(hcd_to_ohci(hcd)); 341*efe7daf2SSergei Shtylyov 342*efe7daf2SSergei Shtylyov irq = platform_get_irq(pdev, 0); 343*efe7daf2SSergei Shtylyov if (irq < 0) { 344*efe7daf2SSergei Shtylyov error = -ENODEV; 345*efe7daf2SSergei Shtylyov goto err4; 346*efe7daf2SSergei Shtylyov } 347*efe7daf2SSergei Shtylyov error = usb_add_hcd(hcd, irq, IRQF_DISABLED); 348*efe7daf2SSergei Shtylyov if (error) 349*efe7daf2SSergei Shtylyov goto err4; 350*efe7daf2SSergei Shtylyov 351*efe7daf2SSergei Shtylyov if (hub->ocic_notify) { 352*efe7daf2SSergei Shtylyov error = hub->ocic_notify(ohci_da8xx_ocic_handler); 353*efe7daf2SSergei Shtylyov if (!error) 354*efe7daf2SSergei Shtylyov return 0; 355*efe7daf2SSergei Shtylyov } 356*efe7daf2SSergei Shtylyov 357*efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 358*efe7daf2SSergei Shtylyov err4: 359*efe7daf2SSergei Shtylyov iounmap(hcd->regs); 360*efe7daf2SSergei Shtylyov err3: 361*efe7daf2SSergei Shtylyov release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 362*efe7daf2SSergei Shtylyov err2: 363*efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 364*efe7daf2SSergei Shtylyov err1: 365*efe7daf2SSergei Shtylyov clk_put(usb20_clk); 366*efe7daf2SSergei Shtylyov err0: 367*efe7daf2SSergei Shtylyov clk_put(usb11_clk); 368*efe7daf2SSergei Shtylyov return error; 369*efe7daf2SSergei Shtylyov } 370*efe7daf2SSergei Shtylyov 371*efe7daf2SSergei Shtylyov /** 372*efe7daf2SSergei Shtylyov * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs 373*efe7daf2SSergei Shtylyov * @dev: USB Host Controller being removed 374*efe7daf2SSergei Shtylyov * Context: !in_interrupt() 375*efe7daf2SSergei Shtylyov * 376*efe7daf2SSergei Shtylyov * Reverses the effect of usb_hcd_da8xx_probe(), first invoking 377*efe7daf2SSergei Shtylyov * the HCD's stop() method. It is always called from a thread 378*efe7daf2SSergei Shtylyov * context, normally "rmmod", "apmd", or something similar. 379*efe7daf2SSergei Shtylyov */ 380*efe7daf2SSergei Shtylyov static inline void 381*efe7daf2SSergei Shtylyov usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) 382*efe7daf2SSergei Shtylyov { 383*efe7daf2SSergei Shtylyov struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; 384*efe7daf2SSergei Shtylyov 385*efe7daf2SSergei Shtylyov hub->ocic_notify(NULL); 386*efe7daf2SSergei Shtylyov usb_remove_hcd(hcd); 387*efe7daf2SSergei Shtylyov iounmap(hcd->regs); 388*efe7daf2SSergei Shtylyov release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 389*efe7daf2SSergei Shtylyov usb_put_hcd(hcd); 390*efe7daf2SSergei Shtylyov clk_put(usb20_clk); 391*efe7daf2SSergei Shtylyov clk_put(usb11_clk); 392*efe7daf2SSergei Shtylyov } 393*efe7daf2SSergei Shtylyov 394*efe7daf2SSergei Shtylyov static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev) 395*efe7daf2SSergei Shtylyov { 396*efe7daf2SSergei Shtylyov return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev); 397*efe7daf2SSergei Shtylyov } 398*efe7daf2SSergei Shtylyov 399*efe7daf2SSergei Shtylyov static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev) 400*efe7daf2SSergei Shtylyov { 401*efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 402*efe7daf2SSergei Shtylyov 403*efe7daf2SSergei Shtylyov usb_hcd_da8xx_remove(hcd, dev); 404*efe7daf2SSergei Shtylyov platform_set_drvdata(dev, NULL); 405*efe7daf2SSergei Shtylyov 406*efe7daf2SSergei Shtylyov return 0; 407*efe7daf2SSergei Shtylyov } 408*efe7daf2SSergei Shtylyov 409*efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 410*efe7daf2SSergei Shtylyov static int ohci_da8xx_suspend(struct platform_device *dev, pm_message_t message) 411*efe7daf2SSergei Shtylyov { 412*efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 413*efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 414*efe7daf2SSergei Shtylyov 415*efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 416*efe7daf2SSergei Shtylyov msleep(5); 417*efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 418*efe7daf2SSergei Shtylyov 419*efe7daf2SSergei Shtylyov ohci_da8xx_clock(0); 420*efe7daf2SSergei Shtylyov hcd->state = HC_STATE_SUSPENDED; 421*efe7daf2SSergei Shtylyov dev->dev.power.power_state = PMSG_SUSPEND; 422*efe7daf2SSergei Shtylyov return 0; 423*efe7daf2SSergei Shtylyov } 424*efe7daf2SSergei Shtylyov 425*efe7daf2SSergei Shtylyov static int ohci_da8xx_resume(struct platform_device *dev) 426*efe7daf2SSergei Shtylyov { 427*efe7daf2SSergei Shtylyov struct usb_hcd *hcd = platform_get_drvdata(dev); 428*efe7daf2SSergei Shtylyov struct ohci_hcd *ohci = hcd_to_ohci(hcd); 429*efe7daf2SSergei Shtylyov 430*efe7daf2SSergei Shtylyov if (time_before(jiffies, ohci->next_statechange)) 431*efe7daf2SSergei Shtylyov msleep(5); 432*efe7daf2SSergei Shtylyov ohci->next_statechange = jiffies; 433*efe7daf2SSergei Shtylyov 434*efe7daf2SSergei Shtylyov ohci_da8xx_clock(1); 435*efe7daf2SSergei Shtylyov dev->dev.power.power_state = PMSG_ON; 436*efe7daf2SSergei Shtylyov usb_hcd_resume_root_hub(hcd); 437*efe7daf2SSergei Shtylyov return 0; 438*efe7daf2SSergei Shtylyov } 439*efe7daf2SSergei Shtylyov #endif 440*efe7daf2SSergei Shtylyov 441*efe7daf2SSergei Shtylyov /* 442*efe7daf2SSergei Shtylyov * Driver definition to register with platform structure. 443*efe7daf2SSergei Shtylyov */ 444*efe7daf2SSergei Shtylyov static struct platform_driver ohci_hcd_da8xx_driver = { 445*efe7daf2SSergei Shtylyov .probe = ohci_hcd_da8xx_drv_probe, 446*efe7daf2SSergei Shtylyov .remove = ohci_hcd_da8xx_drv_remove, 447*efe7daf2SSergei Shtylyov .shutdown = usb_hcd_platform_shutdown, 448*efe7daf2SSergei Shtylyov #ifdef CONFIG_PM 449*efe7daf2SSergei Shtylyov .suspend = ohci_da8xx_suspend, 450*efe7daf2SSergei Shtylyov .resume = ohci_da8xx_resume, 451*efe7daf2SSergei Shtylyov #endif 452*efe7daf2SSergei Shtylyov .driver = { 453*efe7daf2SSergei Shtylyov .owner = THIS_MODULE, 454*efe7daf2SSergei Shtylyov .name = "ohci", 455*efe7daf2SSergei Shtylyov }, 456*efe7daf2SSergei Shtylyov }; 457