* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2000-2005 David Brownell
* (C) Copyright 2002 Hewlett-Packard Company
*
* OMAP Bus Glue
*
- * Written by Christopher Hoover <ch@hpl.hp.com>
- * Based on fragments of previous driver by Russell King et al.
- *
- * Modified for OMAP from ohci-sa1111.c by Tony Lindgren <tony@atomide.com>
+ * Modified for OMAP by Tony Lindgren <tony@atomide.com>
* Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
+ * and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
*
* This file is licenced under the GPL.
*/
#include <asm/arch/gpio.h>
#include <asm/arch/fpga.h>
#include <asm/arch/usb.h>
+#include <asm/hardware/clock.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
-#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);
+#ifdef CONFIG_TPS65010
+#include <asm/arch/tps65010.h>
+#else
-/*
- * OHCI clock initialization for OMAP-1510 and 1610
- */
-static int omap_ohci_clock_power(int on)
-{
- if (on) {
- /* for 1510, 48MHz DPLL is set up in usb init */
+#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)
{
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)
& ~((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;
{
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");
/* 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();
}
#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->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.
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);
}
}
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 = usb_create_hcd (driver);
- 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);
+
+ hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start);
+
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
- 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) {
- dev_dbg(&pdev->dev, "pool alloc fail\n");
- goto err2;
- }
-
- retval = request_irq (hcd->irq, usb_hcd_irq,
- SA_INTERRUPT, hcd->driver->description, hcd);
- if (retval != 0) {
- dev_dbg(&pdev->dev, "request_irq failed\n");
- retval = -EBUSY;
- goto err3;
- }
-
- dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
-
- hcd->self.bus_name = pdev->dev.bus_id;
- usb_register_bus (&hcd->self);
-
- if ((retval = driver->start (hcd)) < 0)
- {
- usb_hcd_omap_remove(hcd, pdev);
+ retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT);
+ if (retval == 0)
return retval;
- }
-
- return 0;
- err3:
- hcd_buffer_destroy (hcd);
- err2:
- dev_set_drvdata(&pdev->dev, NULL);
- usb_put_hcd(hcd);
- err1:
omap_stop_hc(pdev);
-
- release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1);
-
+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 */
/**
*/
void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
- 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);
-
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);
}
/*-------------------------------------------------------------------------*/
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
- if ((ret = ohci_init(ohci)) < 0)
- return ret;
-
config = hcd->self.controller->platform_data;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
if ((ret = ohci_run (ohci)) < 0) {
- err ("can't start %s", hcd->self.bus_name);
+ dev_err(hcd->self.controller, "can't start\n");
ohci_stop (hcd);
return ret;
}
* generic hardware linkage
*/
.irq = ohci_irq,
- .flags = HCD_USB11,
+ .flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
/*-------------------------------------------------------------------------*/
-#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).
+ *
+ * FIXME: above comment is not right, and code is wrong, too :-(.
*/
-static int ohci_omap_suspend(struct device *dev, u32 state, u32 level)
+static int ohci_omap_suspend(struct device *dev, pm_message_t state, u32 level)
{
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = -EINVAL;
+ if (level != SUSPEND_POWER_DOWN)
+ return 0;
if (state <= dev->power.power_state)
return 0;
status = ohci_hub_suspend(ohci_to_hcd(ohci));
if (status == 0) {
if (state >= 4) {
- /* power off + reset */
- OTG_SYSCON_2_REG &= ~UHOST_EN;
+ omap_ohci_clock_power(0);
ohci_to_hcd(ohci)->self.root_hub->state =
USB_STATE_SUSPENDED;
state = 4;
}
- ohci_to_hcd(ohci)->state = HCD_STATE_SUSPENDED;
+ ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
dev->power.power_state = state;
}
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = 0;
+ if (level != RESUME_POWER_ON)
+ return 0;
+
switch (dev->power.power_state) {
case 0:
break;
if (time_before(jiffies, ohci->next_statechange))
msleep(5);
ohci->next_statechange = jiffies;
- OTG_SYSCON_2_REG |= UHOST_EN;
+ omap_ohci_clock_power(1);
/* FALLTHROUGH */
default:
dev_dbg(dev, "resume from %d\n", dev->power.power_state);
.bus = &platform_bus_type,
.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