X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fohci-omap.c;h=d133ff22a4a7c55f81251d18061b95a3b3c03be0;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=eba974d5ccdbd231053ac5dcb60fdca26dbf70fd;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index eba974d5c..d133ff22a 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -2,28 +2,30 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2000-2004 David Brownell * (C) Copyright 2002 Hewlett-Packard Company * * OMAP Bus Glue * * Written by Christopher Hoover - * Based on fragments of previous driver by Rusell King et al. + * Based on fragments of previous driver by Russell King et al. * * Modified for OMAP from ohci-sa1111.c by Tony Lindgren * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc. * * This file is licenced under the GPL. */ - + #include -#include #include +#include -#include #include #include #include +#include +#include +#include #include "ohci-omap.h" @@ -34,123 +36,54 @@ extern int usb_disabled(void); extern int ocpi_enable(void); -/* - * Use the first port only by default. Override with hmc_mode option. - * - * NOTE: Many OMAP-1510 Innovators supposedly have bad wiring for the USB ports - * 1 & 2, so only port 0 will work. To use the OHCI on the first port, use - * the Innovator USB client cable with a client-to-client connector and modify - * either the cable or the hub to feed 5V VBUS back to Innovator. VBUS should - * be the red lead in the cable. - * - * To mount USB hard disk as root, see the patch for do_mounts.c that tries - * remounting the root, and use root=0801 if your root is on sda1. Does not - * work with devfs. - */ -static int default_hmc_mode = 16; -static int hmc_mode = 1234; - -/* - * Set the USB host pin multiplexing and the selected HMC mode - */ -static int omap_usb_set_hmc_mode(int hmc_mode) -{ - unsigned int val; - - switch (hmc_mode) { - case 0: - /* 0: function, 1: disabled, 2: disabled */ - omap_cfg_reg(W4_USB_PUEN); - omap_cfg_reg(R18_1510_USB_GPIO0); - break; - case 4: - /* 0: function 1: host 2: host */ - omap_cfg_reg(usb1_speed); - omap_cfg_reg(usb1_susp); - omap_cfg_reg(usb1_seo); - omap_cfg_reg(usb1_txen); - omap_cfg_reg(usb1_txd); - omap_cfg_reg(usb1_vp); - omap_cfg_reg(usb1_vm); - omap_cfg_reg(usb1_rcv); - omap_cfg_reg(usb2_susp); - omap_cfg_reg(usb2_seo); - omap_cfg_reg(usb2_txen); - omap_cfg_reg(usb2_txd); - omap_cfg_reg(usb2_vp); - omap_cfg_reg(usb2_vm); - omap_cfg_reg(usb2_rcv); - break; - case 16: - /* 0: host, 1: disabled, 2: disabled */ - omap_cfg_reg(W9_USB0_TXEN); - omap_cfg_reg(AA9_USB0_VP); - omap_cfg_reg(Y5_USB0_RCV); - omap_cfg_reg(R9_USB0_VM); - omap_cfg_reg(V6_USB0_TXD); - omap_cfg_reg(W5_USB0_SE0); - break; - default: - printk("Unknown USB host configuration: %i\n", hmc_mode); - return -ENODEV; - } - - /* Write the selected HMC mode */ - val = readl(MOD_CONF_CTRL_0) & ~HMC_CLEAR; - val |= (hmc_mode << 1); - writel(val, MOD_CONF_CTRL_0); - - return 0; -} - /* * OHCI clock initialization for OMAP-1510 and 1610 */ static int omap_ohci_clock_power(int on) { if (on) { - if (cpu_is_omap_1510()) { + if (cpu_is_omap1510()) { /* Use DPLL, not APLL */ - writel(readl(ULPD_APLL_CTRL_REG) & ~APLL_NDPLL_SWITCH, - ULPD_APLL_CTRL_REG); + omap_writel(omap_readl(ULPD_APLL_CTRL) & ~APLL_NDPLL_SWITCH, + ULPD_APLL_CTRL); /* Enable DPLL */ - writel(readl(ULPD_DPLL_CTRL_REG) | DPLL_PLL_ENABLE, - ULPD_DPLL_CTRL_REG); + omap_writel(omap_readl(ULPD_DPLL_CTRL) | DPLL_PLL_ENABLE, + ULPD_DPLL_CTRL); /* Software request for USB 48MHz clock */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_REQ_REG_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_REQ_REG_REQ, + ULPD_SOFT_REQ); - while (!(readl(ULPD_DPLL_CTRL_REG) & DPLL_LOCK)); + while (!(omap_readl(ULPD_DPLL_CTRL) & DPLL_LOCK)); } - if (cpu_is_omap_1610()) { + if (cpu_is_omap16xx()) { /* Enable OHCI */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_OTG_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_OTG_REQ, + ULPD_SOFT_REQ); /* USB host clock request if not using OTG */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_REQ, + ULPD_SOFT_REQ); - writel(readl(ULPD_STATUS_REQ_REG) | USB_HOST_DPLL_REQ, - ULPD_STATUS_REQ_REG); + omap_writel(omap_readl(ULPD_STATUS_REQ) | USB_HOST_DPLL_REQ, + ULPD_STATUS_REQ); } /* Enable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) | USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); + omap_writel(omap_readl(ULPD_CLOCK_CTRL) | USB_MCLK_EN, + ULPD_CLOCK_CTRL); - writel(readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), + omap_writel(omap_readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), ARM_IDLECT2); - writel(readl(MOD_CONF_CTRL_0) | USB_HOST_HHC_UHOST_EN, + omap_writel(omap_readl(MOD_CONF_CTRL_0) | USB_HOST_HHC_UHOST_EN, MOD_CONF_CTRL_0); } else { /* Disable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) & ~USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); + omap_writel(omap_readl(ULPD_CLOCK_CTRL) & ~USB_MCLK_EN, + ULPD_CLOCK_CTRL); /* FIXME: The DPLL stays on for now */ } @@ -164,13 +97,19 @@ static int omap_ohci_clock_power(int on) static int omap_ohci_transceiver_power(int on) { if (on) { - if (omap_is_innovator()) - writel(readl(OMAP1510_FPGA_HOST_CTRL) | 0x20, - OMAP1510_FPGA_HOST_CTRL); + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) | 0x20, + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) { + /* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */ + } } else { - if (omap_is_innovator()) - writel(readl(OMAP1510_FPGA_HOST_CTRL) & ~0x20, - OMAP1510_FPGA_HOST_CTRL); + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~0x20, + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) { + /* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */ + } } return 0; @@ -182,10 +121,10 @@ static int omap_ohci_transceiver_power(int on) static int omap_1510_local_bus_power(int on) { if (on) { - writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); + omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); udelay(200); } else { - writel(0, OMAP1510_LB_MMU_CTL); + omap_writel(0, OMAP1510_LB_MMU_CTL); } return 0; @@ -194,8 +133,8 @@ static int omap_1510_local_bus_power(int on) /* * OMAP-1510 specific Local Bus initialization * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. - * See also arch/mach-omap/memory.h for __virt_to_bus() and - * __bus_to_virt() which need to match with the physical + * See also arch/mach-omap/memory.h for __virt_to_dma() and + * __dma_to_virt() which need to match with the physical * Local Bus address below. */ static int omap_1510_local_bus_init(void) @@ -203,125 +142,115 @@ static int omap_1510_local_bus_init(void) unsigned int tlb; unsigned long lbaddr, physaddr; - writel((readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, + omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, OMAP1510_LB_CLOCK_DIV); /* Configure the Local Bus MMU table */ for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) { lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; physaddr = tlb * 0x00100000 + PHYS_OFFSET; - writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); - writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, + omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); + omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, OMAP1510_LB_MMU_CAM_L); - writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); - writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); - writel(tlb << 4, OMAP1510_LB_MMU_LCK); - writel(0x1, OMAP1510_LB_MMU_LD_TLB); + omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); + omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); + omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK); + omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB); } /* Enable the walking table */ - writel(readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); + omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); udelay(200); return 0; } -/* - * OMAP-1610 specific hardware initialization - * - * Intended to configure OMAP-1610 USB host and OTG ports depending on - * the HMC mode selected. - * - * FIXME: Currently only supports alternate ping group 2 mode, should - * be easy to modify for other configurations once there is some - * hardware to test with. - */ -static int omap_1610_usb_init(int mode) -{ - u_int val = 0; - - /* Configure the OMAP transceiver settings */ - val |= (1 << 8); /* CONF_USB2_UNI TRM p 15-205*/ - val |= (4 << 4); /* TRM p 5-59, p 15-157 (1224) */ +#ifdef CONFIG_USB_OTG - //val |= (1 << 3); /* Isolate integrated transceiver from port 0 */ - val |= (1 << 2); /* Disable pulldown on integrated transceiver DM */ - val |= (1 << 1); /* Disable pulldown on integraded transceiver DP */ +static void start_hnp(struct ohci_hcd *ohci) +{ + const unsigned port = ohci->hcd.self.otg_port - 1; + unsigned long flags; - writel(val, USB_TRANSCEIVER_CTRL); + otg_start_hnp(ohci->transceiver); - /* Set the USB0_TRX_MODE */ - val = 0; - val &= ~OTG_IDLE_EN; - val &= ~DEV_IDLE_EN; - val &= ~(7 << 16); /* Clear USB0_TRX_MODE */ - val |= (3 << 16); /* 0 or 3, 6-wire DAT/SE0, TRM p 15-159 */ - writel(val, OTG_SYSCON_1); + local_irq_save(flags); + ohci->transceiver->state = OTG_STATE_A_SUSPEND; + writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); + OTG_CTRL_REG &= ~OTG_A_BUSREQ; + local_irq_restore(flags); +} - /* - * Control via OTG, see TRM p 15-163 - */ - val = 0; - //val |= 1; /* REVISIT: Enable OTG = 1 */ +#endif - /* Control via OTG */ - val &= ~HMC_PADEN; - val &= ~OTG_PADEN; - val |= UHOST_EN; +/*-------------------------------------------------------------------------*/ - val &= ~0x3f; /* Clear HMC mode */ - val |= mode; /* Set HMC mode */ - val &= ~(7 << 16); /* Clear ASE0_BRST */ - val |= (4 << 16); /* Must be 4 */ - val |= USBX_SYNCHRO; /* Must be set */ - val |= SRP_VBUS; - writel(val, OTG_SYSCON_2); +static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) +{ + struct omap_usb_config *config = pdev->dev.platform_data; + int need_transceiver = (config->otg != 0); - /* Enable OTG idle */ - //writel(readl(OTG_SYSCON_1) | OTG_IDLE_EN, OTG_SYSCON_1); + dev_dbg(&pdev->dev, "starting USB Controller\n"); - return 0; -} + if (config->otg) { + ohci->hcd.self.otg_port = config->otg; + /* default/minimum OTG power budget: 8 mA */ + ohci->power_budget = 8; + } -/*-------------------------------------------------------------------------*/ + /* boards can use OTG transceivers in non-OTG modes */ + need_transceiver = need_transceiver + || machine_is_omap_h2(); -static void omap_start_hc(struct omap_dev *dev) -{ - printk(KERN_DEBUG __FILE__ - ": starting OMAP OHCI USB Controller\n"); + if (cpu_is_omap16xx()) + ocpi_enable(); - /* - * Set the HMC mode for the USB ports - */ -#if 0 - /* See note about the Innovator wiring above */ - if (omap_is_innovator()) - hmc_mode = 4; /* 0: function 1: host 2: host */ +#ifdef CONFIG_ARCH_OMAP_OTG + if (need_transceiver) { + ohci->transceiver = otg_get_transceiver(); + if (ohci->transceiver) { + int status = otg_set_host(ohci->transceiver, + &ohci->hcd.self); + dev_dbg(&pdev->dev, "init %s transceiver, status %d\n", + ohci->transceiver->label, status); + if (status) { + if (ohci->transceiver) + put_device(ohci->transceiver->dev); + return status; + } + } else { + dev_err(&pdev->dev, "can't find transceiver\n"); + return -ENODEV; + } + } #endif - if (cpu_is_omap_1610()) - ocpi_enable(); - - omap_usb_set_hmc_mode(hmc_mode); + if (machine_is_omap_osk()) { + omap_request_gpio(9); + omap_set_gpio_direction(9, 1); + omap_set_gpio_dataout(9, 1); + } omap_ohci_clock_power(1); + omap_ohci_transceiver_power(1); - if (cpu_is_omap_1510()) { + if (cpu_is_omap1510()) { omap_1510_local_bus_power(1); omap_1510_local_bus_init(); } - if (cpu_is_omap_1610()) - omap_1610_usb_init(hmc_mode); + /* board init will have already handled HMC and mux setup. + * any external transceiver should already be initialized + * too, so all configured ports use the right signaling now. + */ - //omap_enable_device(dev); + return 0; } -static void omap_stop_hc(struct omap_dev *dev) +static void omap_stop_hc(struct platform_device *pdev) { - printk(KERN_DEBUG __FILE__ - ": stopping OMAP OHCI USB Controller\n"); + dev_dbg(&pdev->dev, "stopping USB Controller\n"); /* * FIXME: Put the USB host controller into reset. @@ -337,16 +266,7 @@ static void omap_stop_hc(struct omap_dev *dev) /*-------------------------------------------------------------------------*/ -static irqreturn_t usb_hcd_omap_hcim_irq (int irq, void *__hcd, struct pt_regs * r) -{ - struct usb_hcd *hcd = __hcd; - - return usb_hcd_irq(irq, hcd, r); -} - -/*-------------------------------------------------------------------------*/ - -void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *); +void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -359,59 +279,71 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *); * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * - * Store this function in the HCD's struct pci_driver as probe(). */ int usb_hcd_omap_probe (const struct hc_driver *driver, - struct usb_hcd **hcd_out, - struct omap_dev *dev) + struct platform_device *pdev) { int retval; struct usb_hcd *hcd = 0; + struct ohci_hcd *ohci; - if (!request_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1, hcd_name)) { - dbg("request_mem_region failed"); - return -EBUSY; + if (pdev->num_resources != 2) { + printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", + pdev->num_resources); + return -ENODEV; } - omap_start_hc(dev); + if (pdev->resource[0].flags != IORESOURCE_MEM + || pdev->resource[1].flags != IORESOURCE_IRQ) { + printk(KERN_ERR "hcd probe: invalid resource type\n"); + return -ENODEV; + } + + if (!request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } hcd = driver->hcd_alloc (); if (hcd == NULL){ - dbg ("hcd_alloc failed"); + dev_dbg(&pdev->dev, "hcd_alloc failed\n"); retval = -ENOMEM; goto err1; } + dev_set_drvdata(&pdev->dev, hcd); + ohci = hcd_to_ohci(hcd); hcd->driver = (struct hc_driver *) driver; hcd->description = driver->description; - hcd->irq = dev->irq[0]; - hcd->regs = dev->mapbase; - hcd->self.controller = &dev->dev; + hcd->irq = pdev->resource[1].start; + hcd->regs = (void *)pdev->resource[0].start; + hcd->self.controller = &pdev->dev; + + retval = omap_start_hc(ohci, pdev); + if (retval < 0) + goto err2; retval = hcd_buffer_create (hcd); if (retval != 0) { - dbg ("pool alloc fail"); + dev_dbg(&pdev->dev, "pool alloc fail\n"); goto err1; } - retval = request_irq (hcd->irq, - usb_hcd_omap_hcim_irq, + retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT, hcd->description, hcd); if (retval != 0) { - dbg("request_irq failed"); + dev_dbg(&pdev->dev, "request_irq failed\n"); retval = -EBUSY; goto err2; } - info ("%s (OMAP) at 0x%p, irq %d\n", - hcd->description, hcd->regs, hcd->irq); + dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = (void *) hcd; - hcd->self.bus_name = "omap"; + hcd->self.bus_name = pdev->dev.bus_id; hcd->product_desc = "OMAP OHCI"; INIT_LIST_HEAD (&hcd->dev_list); @@ -419,11 +351,10 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, if ((retval = driver->start (hcd)) < 0) { - usb_hcd_omap_remove(hcd, dev); + usb_hcd_omap_remove(hcd, pdev); return retval; } - *hcd_out = hcd; return 0; err2: @@ -431,10 +362,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, if (hcd) driver->hcd_free(hcd); err1: - omap_stop_hc(dev); + omap_stop_hc(pdev); - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + dev_set_drvdata(&pdev->dev, 0); return retval; } @@ -452,26 +385,27 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, * context, normally "rmmod", "apmd", or something similar. * */ -void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) +void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) { - struct usb_device *hub; void *base; - info ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + dev_info(&pdev->dev, "remove: state %x\n", hcd->state); if (in_interrupt ()) BUG (); - hub = hcd->self.root_hub; hcd->state = USB_STATE_QUIESCING; - dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); - usb_disconnect (&hub); + dev_dbg(&pdev->dev, "roothub graceful disconnect\n"); + usb_disconnect (&hcd->self.root_hub); hcd->driver->stop (hcd); hcd_buffer_destroy (hcd); hcd->state = USB_STATE_HALT; + if (machine_is_omap_osk()) + omap_free_gpio(9); + free_irq (hcd->irq, hcd); usb_deregister_bus (&hcd->self); @@ -479,9 +413,10 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) base = hcd->regs; hcd->driver->hcd_free (hcd); - omap_stop_hc(dev); + omap_stop_hc(pdev); - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); } /*-------------------------------------------------------------------------*/ @@ -489,11 +424,13 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) static int __devinit ohci_omap_start (struct usb_hcd *hcd) { + struct omap_usb_config *config; struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_consistent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma); + config = hcd->self.controller->platform_data; + ohci->hcca = dma_alloc_coherent (hcd->self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); if (!ohci->hcca) return -ENOMEM; @@ -503,6 +440,10 @@ ohci_omap_start (struct usb_hcd *hcd) return ret; } ohci->regs = hcd->regs; + + if (config->otg || config->rwc) + writel(OHCI_CTRL_RWC, &ohci->regs->control); + if (hc_reset (ohci) < 0) { ohci_stop (hcd); return -ENODEV; @@ -513,6 +454,9 @@ ohci_omap_start (struct usb_hcd *hcd) ohci_stop (hcd); return -EBUSY; } + if (ohci->power_budget) + hub_set_power_budget(ohci->hcd.self.root_hub, + ohci->power_budget); create_debug_files (ohci); #ifdef DEBUG @@ -536,10 +480,6 @@ static const struct hc_driver ohci_omap_hc_driver = { * basic lifecycle operations */ .start = ohci_omap_start, -#ifdef CONFIG_PM - /* suspend: ohci_omap_suspend, -- tbd */ - /* resume: ohci_omap_resume, -- tbd */ -#endif .stop = ohci_stop, /* @@ -565,104 +505,134 @@ static const struct hc_driver ohci_omap_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif + .start_port_reset = ohci_start_port_reset, }; /*-------------------------------------------------------------------------*/ -static int ohci_hcd_omap_drv_probe(struct omap_dev *dev) +static int ohci_hcd_omap_drv_probe(struct device *dev) { - struct usb_hcd *hcd = NULL; - int ret; + return usb_hcd_omap_probe(&ohci_omap_hc_driver, + to_platform_device(dev)); +} - if (usb_disabled()) - return -ENODEV; +static int ohci_hcd_omap_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + usb_hcd_omap_remove(hcd, pdev); + if (ohci->transceiver) { + (void) otg_set_host(ohci->transceiver, 0); + put_device(ohci->transceiver->dev); + } + dev_set_drvdata(dev, NULL); - ret = usb_hcd_omap_probe(&ohci_omap_hc_driver, &hcd, dev); + return 0; +} + +/*-------------------------------------------------------------------------*/ - if (ret == 0) - omap_set_drvdata(dev, hcd); +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) - return ret; +/* states match PCI usage, always suspending the root hub except that + * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset). + */ + +static int ohci_omap_suspend(struct device *dev, u32 state, u32 level) +{ + struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); + int status = -EINVAL; + + if (state <= dev->power.power_state) + return 0; + + dev_dbg(dev, "suspend to %d\n", state); + down(&ohci->hcd.self.root_hub->serialize); + status = ohci_hub_suspend(&ohci->hcd); + if (status == 0) { + if (state >= 4) { + /* power off + reset */ + OTG_SYSCON_2_REG &= ~UHOST_EN; + ohci->hcd.self.root_hub->state = USB_STATE_SUSPENDED; + state = 4; + } + ohci->hcd.state = HCD_STATE_SUSPENDED; + dev->power.power_state = state; + } + up(&ohci->hcd.self.root_hub->serialize); + return status; } -static int ohci_hcd_omap_drv_remove(struct omap_dev *dev) +static int ohci_omap_resume(struct device *dev, u32 level) { - struct usb_hcd *hcd = omap_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); + int status = 0; - usb_hcd_omap_remove(hcd, dev); + switch (dev->power.power_state) { + case 0: + break; + case 4: + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + OTG_SYSCON_2_REG |= UHOST_EN; + /* FALLTHROUGH */ + default: + dev_dbg(dev, "resume from %d\n", dev->power.power_state); +#ifdef CONFIG_USB_SUSPEND + /* get extra cleanup even if remote wakeup isn't in use */ + status = usb_resume_device(ohci->hcd.self.root_hub); +#else + down(&ohci->hcd.self.root_hub->serialize); + status = ohci_hub_resume(&ohci->hcd); + up(&ohci->hcd.self.root_hub->serialize); +#endif + if (status == 0) + dev->power.power_state = 0; + break; + } + return status; +} - omap_set_drvdata(dev, NULL); +#endif - return 0; -} +/*-------------------------------------------------------------------------*/ /* * Driver definition to register with the OMAP bus */ -static struct omap_driver ohci_hcd_omap_driver = { - .drv = { - .name = OMAP_OHCI_NAME, - }, - .devid = OMAP_OCP_DEVID_USB, - .busid = OMAP_BUS_OCP, - .clocks = 0, +static struct device_driver ohci_hcd_omap_driver = { + .name = "ohci", + .bus = &platform_bus_type, .probe = ohci_hcd_omap_drv_probe, .remove = ohci_hcd_omap_drv_remove, -}; - -/* Any dma_mask must be set for OHCI to work */ -static u64 omap_dmamask = 0xffffffffUL; - -/* - * Device definition to match the driver above - */ -static struct omap_dev ohci_hcd_omap_device = { - .name = OMAP_OHCI_NAME, - .devid = OMAP_OCP_DEVID_USB, - .busid = OMAP_BUS_OCP, - .mapbase = (void *)OMAP_OHCI_BASE, - .dma_mask = &omap_dmamask, /* Needed only for OHCI */ - .res = { - .start = OMAP_OHCI_BASE, - .end = OMAP_OHCI_BASE + OMAP_OHCI_SIZE, - }, - .irq = { - INT_USB_HHC_1, - }, +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) + .suspend = ohci_omap_suspend, + .resume = ohci_omap_resume, +#endif }; static int __init ohci_hcd_omap_init (void) { - int ret; - - dbg (DRIVER_INFO " (OMAP)"); - dbg ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - if (hmc_mode < 0 || hmc_mode > 25) - hmc_mode = default_hmc_mode; - - /* Register the driver with OMAP bus */ - ret = omap_driver_register(&ohci_hcd_omap_driver); - if (ret != 0) + printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name); + if (usb_disabled()) return -ENODEV; - /* Register the device with OMAP bus */ - ret = omap_device_register(&ohci_hcd_omap_device); - if (ret != 0) { - omap_driver_unregister(&ohci_hcd_omap_driver); - return -ENODEV; - } + pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, + sizeof (struct ed), sizeof (struct td)); - return ret; + return driver_register(&ohci_hcd_omap_driver); } -MODULE_PARM(hmc_mode, "hmc_mode"); - static void __exit ohci_hcd_omap_cleanup (void) { - omap_device_unregister(&ohci_hcd_omap_device); - omap_driver_unregister(&ohci_hcd_omap_driver); + driver_unregister(&ohci_hcd_omap_driver); } module_init (ohci_hcd_omap_init);