X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fohci-omap.c;h=ca19abe01c5331437bfb9e25e9324f05686ab964;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=d133ff22a4a7c55f81251d18061b95a3b3c03be0;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index d133ff22a..ca19abe01 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -2,114 +2,102 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2004 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 Russell King et al. - * - * 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 /* SA_INTERRUPT */ +#include +#include +#include + #include #include #include -#include #include #include #include #include #include -#include "ohci-omap.h" + +/* 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 + #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); - -/* - * OHCI clock initialization for OMAP-1510 and 1610 - */ -static int omap_ohci_clock_power(int on) -{ - if (on) { - if (cpu_is_omap1510()) { - /* Use DPLL, not APLL */ - omap_writel(omap_readl(ULPD_APLL_CTRL) & ~APLL_NDPLL_SWITCH, - ULPD_APLL_CTRL); - - /* Enable DPLL */ - omap_writel(omap_readl(ULPD_DPLL_CTRL) | DPLL_PLL_ENABLE, - ULPD_DPLL_CTRL); - - /* Software request for USB 48MHz clock */ - omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_REQ_REG_REQ, - ULPD_SOFT_REQ); +#ifdef CONFIG_TPS65010 +#include +#else - while (!(omap_readl(ULPD_DPLL_CTRL) & DPLL_LOCK)); - } +#define LOW 0 +#define HIGH 1 - if (cpu_is_omap16xx()) { - /* Enable OHCI */ - omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_OTG_REQ, - ULPD_SOFT_REQ); +#define GPIO1 1 - /* USB host clock request if not using OTG */ - omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_REQ, - ULPD_SOFT_REQ); +static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) +{ + return 0; +} - omap_writel(omap_readl(ULPD_STATUS_REQ) | USB_HOST_DPLL_REQ, - ULPD_STATUS_REQ); - } +#endif - /* Enable 48MHz clock to USB */ - omap_writel(omap_readl(ULPD_CLOCK_CTRL) | USB_MCLK_EN, - ULPD_CLOCK_CTRL); +extern int usb_disabled(void); +extern int ocpi_enable(void); - omap_writel(omap_readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), - ARM_IDLECT2); +static struct clk *usb_host_ck; - omap_writel(omap_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_host_ck); + /* guesstimate for T5 == 1x 32K clock + APLL lock time */ + udelay(100); } else { - /* Disable 48MHz clock to USB */ - omap_writel(omap_readl(ULPD_CLOCK_CTRL) & ~USB_MCLK_EN, - ULPD_CLOCK_CTRL); - - /* FIXME: The DPLL stays on for now */ + clk_disable(usb_host_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 (machine_is_omap_innovator() && cpu_is_omap1510()) - fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) | 0x20, + 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()) { - /* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */ - } + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, LOW); } else { if (machine_is_omap_innovator() && cpu_is_omap1510()) - fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~0x20, + 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()) { - /* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */ - } + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, HIGH); } return 0; @@ -169,7 +157,7 @@ static int omap_1510_local_bus_init(void) static void start_hnp(struct ohci_hcd *ohci) { - const unsigned port = ohci->hcd.self.otg_port - 1; + const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; unsigned long flags; otg_start_hnp(ohci->transceiver); @@ -189,18 +177,19 @@ 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); + int ret; dev_dbg(&pdev->dev, "starting USB Controller\n"); if (config->otg) { - ohci->hcd.self.otg_port = config->otg; + ohci_to_hcd(ohci)->self.otg_port = config->otg; /* default/minimum OTG power budget: 8 mA */ - ohci->power_budget = 8; + 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_h2() || machine_is_omap_h3(); if (cpu_is_omap16xx()) ocpi_enable(); @@ -210,7 +199,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) ohci->transceiver = otg_get_transceiver(); if (ohci->transceiver) { int status = otg_set_host(ohci->transceiver, - &ohci->hcd.self); + &ohci_to_hcd(ohci)->self); dev_dbg(&pdev->dev, "init %s transceiver, status %d\n", ohci->transceiver->label, status); if (status) { @@ -225,21 +214,44 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) } #endif - 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_omap1510()) { omap_1510_local_bus_power(1); omap_1510_local_bus_init(); } + if ((ret = ohci_init(ohci)) < 0) + return ret; + + /* board-specific power switching and overcurrent support */ + if (machine_is_omap_osk() || machine_is_omap_innovator()) { + u32 rh = roothub_a (ohci); + + /* power switching (ganged by default) */ + rh &= ~RH_A_NPS; + + /* TPS2045 switch for internal transceiver (port 1) */ + if (machine_is_omap_osk()) { + ohci_to_hcd(ohci)->power_budget = 250; + + 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; + } + + /* FIXME khubd hub requests should manage power switching */ + omap_ohci_transceiver_power(1); + /* 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. @@ -251,16 +263,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) static void omap_stop_hc(struct platform_device *pdev) { dev_dbg(&pdev->dev, "stopping USB Controller\n"); - - /* - * FIXME: Put the USB host controller into reset. - */ - - /* - * FIXME: Stop the USB clock. - */ - //omap_disable_device(dev); - + omap_ohci_clock_power(0); } @@ -283,7 +286,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); 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; @@ -294,85 +297,58 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, } if (pdev->resource[0].flags != IORESOURCE_MEM - || pdev->resource[1].flags != IORESOURCE_IRQ) { + || 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; - } + usb_host_ck = clk_get(0, "usb_hhc_ck"); + if (IS_ERR(usb_host_ck)) + return PTR_ERR(usb_host_ck); - hcd = driver->hcd_alloc (); - if (hcd == NULL){ - dev_dbg(&pdev->dev, "hcd_alloc failed\n"); + 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; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + retval = -EBUSY; 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 = pdev->resource[1].start; - hcd->regs = (void *)pdev->resource[0].start; - hcd->self.controller = &pdev->dev; + hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start); + + ohci = hcd_to_ohci(hcd); + ohci_hcd_init(ohci); retval = omap_start_hc(ohci, pdev); if (retval < 0) goto err2; - retval = hcd_buffer_create (hcd); - if (retval != 0) { - dev_dbg(&pdev->dev, "pool alloc fail\n"); - goto err1; - } - - retval = request_irq (hcd->irq, usb_hcd_irq, - SA_INTERRUPT, hcd->description, hcd); - if (retval != 0) { - dev_dbg(&pdev->dev, "request_irq failed\n"); - retval = -EBUSY; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = -ENXIO; goto err2; } - - 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 = pdev->dev.bus_id; - hcd->product_desc = "OMAP OHCI"; - - INIT_LIST_HEAD (&hcd->dev_list); - usb_register_bus (&hcd->self); - - if ((retval = driver->start (hcd)) < 0) - { - usb_hcd_omap_remove(hcd, pdev); + retval = usb_add_hcd(hcd, irq, SA_INTERRUPT); + if (retval == 0) return retval; - } - - return 0; - err2: - hcd_buffer_destroy (hcd); - if (hcd) - driver->hcd_free(hcd); - err1: omap_stop_hc(pdev); - - release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); - - dev_set_drvdata(&pdev->dev, 0); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); +err0: + clk_put(usb_host_ck); return retval; } -/* may be called without controller electrically present */ /* may be called with controller, bus, and devices active */ /** @@ -387,36 +363,13 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, */ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) { - void *base; - - dev_info(&pdev->dev, "remove: state %x\n", hcd->state); - - if (in_interrupt ()) - BUG (); - - hcd->state = USB_STATE_QUIESCING; - - 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; - + usb_remove_hcd(hcd); if (machine_is_omap_osk()) omap_free_gpio(9); - - free_irq (hcd->irq, hcd); - - usb_deregister_bus (&hcd->self); - - base = hcd->regs; - hcd->driver->hcd_free (hcd); - omap_stop_hc(pdev); - - release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + clk_put(usb_host_ck); } /*-------------------------------------------------------------------------*/ @@ -429,39 +382,14 @@ ohci_omap_start (struct usb_hcd *hcd) int ret; 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; - - 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 (config->otg || config->rwc) writel(OHCI_CTRL_RWC, &ohci->regs->control); - if (hc_reset (ohci) < 0) { - ohci_stop (hcd); - return -ENODEV; - } - - 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; } - if (ohci->power_budget) - hub_set_power_budget(ohci->hcd.self.root_hub, - ohci->power_budget); - create_debug_files (ohci); - -#ifdef DEBUG - ohci_dump (ohci, 1); -#endif return 0; } @@ -469,12 +397,14 @@ 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 @@ -482,12 +412,6 @@ static const struct hc_driver ohci_omap_hc_driver = { .start = ohci_omap_start, .stop = ohci_stop, - /* - * memory lifecycle (except per-request) - */ - .hcd_alloc = ohci_hcd_alloc, - .hcd_free = ohci_hcd_free, - /* * managing i/o requests and associated device resources */ @@ -505,99 +429,65 @@ 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, +#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 device *dev) +static int ohci_hcd_omap_drv_probe(struct platform_device *dev) { - return usb_hcd_omap_probe(&ohci_omap_hc_driver, - to_platform_device(dev)); + return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); } -static int ohci_hcd_omap_drv_remove(struct device *dev) +static int ohci_hcd_omap_drv_remove(struct platform_device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct usb_hcd *hcd = dev_get_drvdata(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci (hcd); - usb_hcd_omap_remove(hcd, pdev); + usb_hcd_omap_remove(hcd, dev); if (ohci->transceiver) { (void) otg_set_host(ohci->transceiver, 0); put_device(ohci->transceiver->dev); } - dev_set_drvdata(dev, NULL); + platform_set_drvdata(dev, NULL); return 0; } /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) +#ifdef CONFIG_PM -/* 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) +static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) { - 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; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(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->power.power_state = PMSG_SUSPEND; + return 0; } -static int ohci_omap_resume(struct device *dev, u32 level) +static int ohci_omap_resume(struct platform_device *dev) { - struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); - int status = 0; - - 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; + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + omap_ohci_clock_power(1); + dev->power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(dev_get_drvdata(dev)); + return 0; } #endif @@ -607,15 +497,17 @@ static int ohci_omap_resume(struct device *dev, u32 level) /* * Driver definition to register with the OMAP bus */ -static struct device_driver ohci_hcd_omap_driver = { - .name = "ohci", - .bus = &platform_bus_type, +static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_drv_probe, .remove = ohci_hcd_omap_drv_remove, -#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) +#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) @@ -627,12 +519,12 @@ static int __init ohci_hcd_omap_init (void) pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); - return driver_register(&ohci_hcd_omap_driver); + return platform_driver_register(&ohci_hcd_omap_driver); } static void __exit ohci_hcd_omap_cleanup (void) { - driver_unregister(&ohci_hcd_omap_driver); + platform_driver_unregister(&ohci_hcd_omap_driver); } module_init (ohci_hcd_omap_init);