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