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