X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fohci-omap.c;h=27be1f9368853925da47e2b64cd18fb3ec59088e;hb=refs%2Fheads%2Fvserver;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..27be1f936 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -2,190 +2,123 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2000-2005 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. + * OMAP Bus Glue * - * Modified for OMAP from ohci-sa1111.c by Tony Lindgren + * Modified for OMAP by Tony Lindgren * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc. + * and on ohci-sa1111.c by Christopher Hoover * * This file is licenced under the GPL. */ - + +#include /* IRQF_DISABLED */ +#include +#include +#include + #include -#include #include +#include -#include -#include #include #include +#include +#include +#include + + +/* OMAP-1510 OHCI has its own MMU for DMA */ +#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */ +#define OMAP1510_LB_CLOCK_DIV 0xfffec10c +#define OMAP1510_LB_MMU_CTL 0xfffec208 +#define OMAP1510_LB_MMU_LCK 0xfffec224 +#define OMAP1510_LB_MMU_LD_TLB 0xfffec228 +#define OMAP1510_LB_MMU_CAM_H 0xfffec22c +#define OMAP1510_LB_MMU_CAM_L 0xfffec230 +#define OMAP1510_LB_MMU_RAM_H 0xfffec234 +#define OMAP1510_LB_MMU_RAM_L 0xfffec238 -#include "ohci-omap.h" #ifndef CONFIG_ARCH_OMAP #error "This file is OMAP bus glue. CONFIG_OMAP must be defined." #endif -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; +#ifdef CONFIG_TPS65010 +#include +#else -/* - * 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; - } +#define LOW 0 +#define HIGH 1 - /* Write the selected HMC mode */ - val = readl(MOD_CONF_CTRL_0) & ~HMC_CLEAR; - val |= (hmc_mode << 1); - writel(val, MOD_CONF_CTRL_0); +#define GPIO1 1 +static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) +{ 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()) { - /* Use DPLL, not APLL */ - writel(readl(ULPD_APLL_CTRL_REG) & ~APLL_NDPLL_SWITCH, - ULPD_APLL_CTRL_REG); - - /* Enable DPLL */ - writel(readl(ULPD_DPLL_CTRL_REG) | DPLL_PLL_ENABLE, - ULPD_DPLL_CTRL_REG); - - /* Software request for USB 48MHz clock */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_REQ_REG_REQ, - ULPD_SOFT_REQ_REG); - - while (!(readl(ULPD_DPLL_CTRL_REG) & DPLL_LOCK)); - } - - if (cpu_is_omap_1610()) { - /* Enable OHCI */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_OTG_REQ, - ULPD_SOFT_REQ_REG); - - /* USB host clock request if not using OTG */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_REQ, - ULPD_SOFT_REQ_REG); - - writel(readl(ULPD_STATUS_REQ_REG) | USB_HOST_DPLL_REQ, - ULPD_STATUS_REQ_REG); - } +#endif - /* Enable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) | USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); +extern int usb_disabled(void); +extern int ocpi_enable(void); - writel(readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), - ARM_IDLECT2); +static struct clk *usb_host_ck; +static struct clk *usb_dc_ck; +static int host_enabled; +static int host_initialized; - writel(readl(MOD_CONF_CTRL_0) | USB_HOST_HHC_UHOST_EN, - MOD_CONF_CTRL_0); +static void omap_ohci_clock_power(int on) +{ + if (on) { + clk_enable(usb_dc_ck); + clk_enable(usb_host_ck); + /* guesstimate for T5 == 1x 32K clock + APLL lock time */ + udelay(100); } else { - /* Disable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) & ~USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); - - /* FIXME: The DPLL stays on for now */ + clk_disable(usb_host_ck); + clk_disable(usb_dc_ck); } - - return 0; } /* - * Hardware specific transceiver power on/off + * Board specific gang-switched transceiver power on/off. + * NOTE: OSK supplies power from DC, not battery. */ 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) + | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, LOW); } 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) + & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, HIGH); } return 0; } +#ifdef CONFIG_ARCH_OMAP15XX /* * OMAP-1510 specific Local Bus clock on/off */ 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 +127,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,154 +136,153 @@ 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; } +#else +#define omap_1510_local_bus_power(x) {} +#define omap_1510_local_bus_init() {} +#endif -/* - * 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) */ - - //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 */ - - writel(val, USB_TRANSCEIVER_CTRL); - - /* 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); - - /* - * Control via OTG, see TRM p 15-163 - */ - val = 0; - //val |= 1; /* REVISIT: Enable OTG = 1 */ - - /* Control via OTG */ - val &= ~HMC_PADEN; - val &= ~OTG_PADEN; - val |= UHOST_EN; +#ifdef CONFIG_USB_OTG - 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 void start_hnp(struct ohci_hcd *ohci) +{ + const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + unsigned long flags; - /* Enable OTG idle */ - //writel(readl(OTG_SYSCON_1) | OTG_IDLE_EN, OTG_SYSCON_1); + otg_start_hnp(ohci->transceiver); - return 0; + 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); } +#endif + /*-------------------------------------------------------------------------*/ -static void omap_start_hc(struct omap_dev *dev) +static int ohci_omap_init(struct usb_hcd *hcd) { - printk(KERN_DEBUG __FILE__ - ": starting OMAP OHCI USB Controller\n"); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct omap_usb_config *config = hcd->self.controller->platform_data; + int need_transceiver = (config->otg != 0); + int ret; - /* - * 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 */ -#endif + dev_dbg(hcd->self.controller, "starting USB Controller\n"); - if (cpu_is_omap_1610()) + if (config->otg) { + ohci_to_hcd(ohci)->self.otg_port = config->otg; + /* default/minimum OTG power budget: 8 mA */ + ohci_to_hcd(ohci)->power_budget = 8; + } + + /* boards can use OTG transceivers in non-OTG modes */ + need_transceiver = need_transceiver + || machine_is_omap_h2() || machine_is_omap_h3(); + + if (cpu_is_omap16xx()) ocpi_enable(); - omap_usb_set_hmc_mode(hmc_mode); +#ifdef CONFIG_ARCH_OMAP_OTG + if (need_transceiver) { + ohci->transceiver = otg_get_transceiver(); + if (ohci->transceiver) { + int status = otg_set_host(ohci->transceiver, + &ohci_to_hcd(ohci)->self); + dev_dbg(hcd->self.controller, "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(hcd->self.controller, "can't find transceiver\n"); + return -ENODEV; + } + } +#endif 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); + if ((ret = ohci_init(ohci)) < 0) + return ret; - //omap_enable_device(dev); -} + /* board-specific power switching and overcurrent support */ + if (machine_is_omap_osk() || machine_is_omap_innovator()) { + u32 rh = roothub_a (ohci); -static void omap_stop_hc(struct omap_dev *dev) -{ - printk(KERN_DEBUG __FILE__ - ": stopping OMAP OHCI USB Controller\n"); + /* power switching (ganged by default) */ + rh &= ~RH_A_NPS; - /* - * FIXME: Put the USB host controller into reset. - */ + /* TPS2045 switch for internal transceiver (port 1) */ + if (machine_is_omap_osk()) { + ohci_to_hcd(ohci)->power_budget = 250; - /* - * FIXME: Stop the USB clock. - */ - //omap_disable_device(dev); + rh &= ~RH_A_NOCP; -} + /* gpio9 for overcurrent detction */ + omap_cfg_reg(W8_1610_GPIO9); + omap_request_gpio(9); + omap_set_gpio_direction(9, 1 /* IN */); + /* for paranoia's sake: disable USB.PUEN */ + omap_cfg_reg(W4_USB_HIGHZ); + } + ohci_writel(ohci, rh, &ohci->regs->roothub.a); + distrust_firmware = 0; + } else if (machine_is_nokia770()) { + /* We require a self-powered hub, which should have + * plenty of power. */ + ohci_to_hcd(ohci)->power_budget = 0; + } -/*-------------------------------------------------------------------------*/ + /* FIXME khubd hub requests should manage power switching */ + omap_ohci_transceiver_power(1); -static irqreturn_t usb_hcd_omap_hcim_irq (int irq, void *__hcd, struct pt_regs * r) -{ - struct usb_hcd *hcd = __hcd; + /* 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. + */ - return usb_hcd_irq(irq, hcd, r); + return 0; } -/*-------------------------------------------------------------------------*/ - -void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *); +static void ohci_omap_stop(struct usb_hcd *hcd) +{ + dev_dbg(hcd->self.controller, "stopping USB Controller\n"); + omap_ohci_clock_power(0); +} -/* configure so an HC device and id are always provided */ -/* always called with process context; sleeping is OK */ +/*-------------------------------------------------------------------------*/ /** * usb_hcd_omap_probe - initialize OMAP-based HCDs @@ -359,87 +291,89 @@ 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) +static int usb_hcd_omap_probe (const struct hc_driver *driver, + struct platform_device *pdev) { - int retval; + int retval, irq; 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; + } - hcd = driver->hcd_alloc (); - if (hcd == NULL){ - dbg ("hcd_alloc failed"); - retval = -ENOMEM; - goto err1; + usb_host_ck = clk_get(0, "usb_hhc_ck"); + if (IS_ERR(usb_host_ck)) + return PTR_ERR(usb_host_ck); + + if (!cpu_is_omap1510()) + usb_dc_ck = clk_get(0, "usb_dc_ck"); + else + usb_dc_ck = clk_get(0, "lb_ck"); + + if (IS_ERR(usb_dc_ck)) { + clk_put(usb_host_ck); + return PTR_ERR(usb_dc_ck); } - hcd->driver = (struct hc_driver *) driver; - hcd->description = driver->description; - hcd->irq = dev->irq[0]; - hcd->regs = dev->mapbase; - hcd->self.controller = &dev->dev; - retval = hcd_buffer_create (hcd); - if (retval != 0) { - dbg ("pool alloc fail"); - goto err1; + hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id); + if (!hcd) { + retval = -ENOMEM; + goto err0; } + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; - retval = request_irq (hcd->irq, - usb_hcd_omap_hcim_irq, - SA_INTERRUPT, hcd->description, hcd); - if (retval != 0) { - dbg("request_irq failed"); + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); retval = -EBUSY; - goto err2; + goto err1; } - info ("%s (OMAP) at 0x%p, irq %d\n", - hcd->description, hcd->regs, hcd->irq); + hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start); - usb_bus_init (&hcd->self); - hcd->self.op = &usb_hcd_operations; - hcd->self.hcpriv = (void *) hcd; - hcd->self.bus_name = "omap"; - hcd->product_desc = "OMAP OHCI"; + ohci = hcd_to_ohci(hcd); + ohci_hcd_init(ohci); - INIT_LIST_HEAD (&hcd->dev_list); - usb_register_bus (&hcd->self); + host_initialized = 0; + host_enabled = 1; - if ((retval = driver->start (hcd)) < 0) - { - usb_hcd_omap_remove(hcd, dev); - return retval; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = -ENXIO; + goto err2; } + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + if (retval) + goto err2; - *hcd_out = hcd; - return 0; - - err2: - hcd_buffer_destroy (hcd); - if (hcd) - driver->hcd_free(hcd); - err1: - omap_stop_hc(dev); + host_initialized = 1; - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + if (!host_enabled) + omap_ohci_clock_power(0); + return 0; +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); +err0: + clk_put(usb_dc_ck); + clk_put(usb_host_ck); return retval; } -/* may be called without controller electrically present */ /* may be called with controller, bus, and devices active */ /** @@ -450,74 +384,47 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, * Reverses the effect of usb_hcd_omap_probe(), first invoking * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. - * */ -void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) +static inline 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); - - if (in_interrupt ()) - BUG (); - - hub = hcd->self.root_hub; - hcd->state = USB_STATE_QUIESCING; + struct ohci_hcd *ohci = hcd_to_ohci (hcd); - dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); - usb_disconnect (&hub); - - hcd->driver->stop (hcd); - hcd_buffer_destroy (hcd); - hcd->state = USB_STATE_HALT; - - free_irq (hcd->irq, hcd); - - usb_deregister_bus (&hcd->self); - - base = hcd->regs; - hcd->driver->hcd_free (hcd); - - omap_stop_hc(dev); - - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + usb_remove_hcd(hcd); + if (ohci->transceiver) { + (void) otg_set_host(ohci->transceiver, 0); + put_device(ohci->transceiver->dev); + } + if (machine_is_omap_osk()) + omap_free_gpio(9); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + clk_put(usb_dc_ck); + clk_put(usb_host_ck); } /*-------------------------------------------------------------------------*/ -static int __devinit +static int 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); - if (!ohci->hcca) - return -ENOMEM; - - memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); - if ((ret = ohci_mem_init (ohci)) < 0) { - ohci_stop (hcd); - return ret; - } - ohci->regs = hcd->regs; - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; + if (!host_enabled) + return 0; + config = hcd->self.controller->platform_data; + if (config->otg || config->rwc) { + ohci->hc_control = OHCI_CTRL_RWC; + writel(OHCI_CTRL_RWC, &ohci->regs->control); } - if (hc_start (ohci) < 0) { - err ("can't start %s", ohci->hcd.self.bus_name); + if ((ret = ohci_run (ohci)) < 0) { + dev_err(hcd->self.controller, "can't start\n"); ohci_stop (hcd); - return -EBUSY; + return ret; } - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } @@ -525,28 +432,22 @@ ohci_omap_start (struct usb_hcd *hcd) static const struct hc_driver ohci_omap_hc_driver = { .description = hcd_name, + .product_desc = "OMAP OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), /* * generic hardware linkage */ .irq = ohci_irq, - .flags = HCD_USB11, + .flags = HCD_USB11 | HCD_MEMORY, /* * basic lifecycle operations */ + .reset = ohci_omap_init, .start = ohci_omap_start, -#ifdef CONFIG_PM - /* suspend: ohci_omap_suspend, -- tbd */ - /* resume: ohci_omap_resume, -- tbd */ -#endif - .stop = ohci_stop, - - /* - * memory lifecycle (except per-request) - */ - .hcd_alloc = ohci_hcd_alloc, - .hcd_free = ohci_hcd_free, + .stop = ohci_omap_stop, + .shutdown = ohci_shutdown, /* * managing i/o requests and associated device resources @@ -565,104 +466,99 @@ static const struct hc_driver ohci_omap_hc_driver = { */ .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_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 platform_device *dev) { - struct usb_hcd *hcd = NULL; - int ret; - - if (usb_disabled()) - return -ENODEV; + return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); +} - ret = usb_hcd_omap_probe(&ohci_omap_hc_driver, &hcd, dev); +static int ohci_hcd_omap_drv_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); - if (ret == 0) - omap_set_drvdata(dev, hcd); + usb_hcd_omap_remove(hcd, dev); + platform_set_drvdata(dev, NULL); - return ret; + return 0; } -static int ohci_hcd_omap_drv_remove(struct omap_dev *dev) +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) { - struct usb_hcd *hcd = omap_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); - usb_hcd_omap_remove(hcd, dev); + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + omap_ohci_clock_power(0); + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; + dev->dev.power.power_state = PMSG_SUSPEND; + return 0; +} + +static int ohci_omap_resume(struct platform_device *dev) +{ + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); - omap_set_drvdata(dev, NULL); + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + omap_ohci_clock_power(1); + dev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(platform_get_drvdata(dev)); return 0; } +#endif + +/*-------------------------------------------------------------------------*/ + /* * 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 platform_driver ohci_hcd_omap_driver = { .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, + .shutdown = usb_hcd_platform_shutdown, +#ifdef CONFIG_PM + .suspend = ohci_omap_suspend, + .resume = ohci_omap_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "ohci", }, }; 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 platform_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); + platform_driver_unregister(&ohci_hcd_omap_driver); } module_init (ohci_hcd_omap_init);