Commit f0a233c0 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: ohci_omap updates

This updates the OMAP OHCI support to match the latest from the
Linux-OMAP tree.  Notable changes from the preceding version:

  - Use the new USB init model (arch/arm/mach-omap/usb.c).

  - The OMAP-specific bus is gone; it now uses platform_device.
    (Update from Tony Lindgren.)

  - Works on the H2 board, which requires the external isp1301
    transceiver even in non-OTG configurations.

  - Adds OTG support.

  - Adds basic power management calls.

The mach-omap/Kconfig file in the main Linux tree is a bit out of
date, it doesn't know about CONFIG_ARCH_OMAP_OTG (for the OMAPs that
have OTG silicon).
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 9fbdfdf7
...@@ -52,6 +52,7 @@ config USB_EHCI_ROOT_HUB_TT ...@@ -52,6 +52,7 @@ config USB_EHCI_ROOT_HUB_TT
config USB_OHCI_HCD config USB_OHCI_HCD
tristate "OHCI HCD support" tristate "OHCI HCD support"
depends on USB depends on USB
select ISP1301_OMAP if MACH_OMAP_H2
---help--- ---help---
The Open Host Controller Interface (OHCI) is a standard for accessing The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's USB 1.1 host controller hardware. It does more in hardware than Intel's
......
...@@ -97,6 +97,7 @@ ...@@ -97,6 +97,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/interrupt.h> /* for in_interrupt () */ #include <linux/interrupt.h> /* for in_interrupt () */
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb_otg.h>
#include "../core/hcd.h" #include "../core/hcd.h"
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */ #include <linux/dmapool.h> /* needed by ohci-mem.c when no PCI */
...@@ -693,7 +694,7 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -693,7 +694,7 @@ static void ohci_stop (struct usb_hcd *hcd)
/* must not be called from interrupt context */ /* must not be called from interrupt context */
#ifdef CONFIG_PM #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
static void mark_children_gone (struct usb_device *dev) static void mark_children_gone (struct usb_device *dev)
{ {
......
...@@ -2,27 +2,30 @@ ...@@ -2,27 +2,30 @@
* OHCI HCD (Host Controller Driver) for USB. * OHCI HCD (Host Controller Driver) for USB.
* *
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company * (C) Copyright 2002 Hewlett-Packard Company
* *
* OMAP Bus Glue * OMAP Bus Glue
* *
* Written by Christopher Hoover <ch@hpl.hp.com> * Written by Christopher Hoover <ch@hpl.hp.com>
* 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 <tony@atomide.com> * Modified for OMAP from ohci-sa1111.c by Tony Lindgren <tony@atomide.com>
* Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc. * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
* *
* This file is licenced under the GPL. * This file is licenced under the GPL.
*/ */
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/bus.h>
#include <asm/arch/hardware.h> #include <asm/arch/hardware.h>
#include <asm/arch/mux.h> #include <asm/arch/mux.h>
#include <asm/arch/irqs.h> #include <asm/arch/irqs.h>
#include <asm/arch/gpio.h>
#include <asm/arch/fpga.h>
#include <asm/arch/usb.h>
#include "ohci-omap.h" #include "ohci-omap.h"
...@@ -33,123 +36,54 @@ ...@@ -33,123 +36,54 @@
extern int usb_disabled(void); extern int usb_disabled(void);
extern int ocpi_enable(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 * OHCI clock initialization for OMAP-1510 and 1610
*/ */
static int omap_ohci_clock_power(int on) static int omap_ohci_clock_power(int on)
{ {
if (on) { if (on) {
if (cpu_is_omap_1510()) { if (cpu_is_omap1510()) {
/* Use DPLL, not APLL */ /* Use DPLL, not APLL */
writel(readl(ULPD_APLL_CTRL_REG) & ~APLL_NDPLL_SWITCH, omap_writel(omap_readl(ULPD_APLL_CTRL) & ~APLL_NDPLL_SWITCH,
ULPD_APLL_CTRL_REG); ULPD_APLL_CTRL);
/* Enable DPLL */ /* Enable DPLL */
writel(readl(ULPD_DPLL_CTRL_REG) | DPLL_PLL_ENABLE, omap_writel(omap_readl(ULPD_DPLL_CTRL) | DPLL_PLL_ENABLE,
ULPD_DPLL_CTRL_REG); ULPD_DPLL_CTRL);
/* Software request for USB 48MHz clock */ /* Software request for USB 48MHz clock */
writel(readl(ULPD_SOFT_REQ_REG) | SOFT_REQ_REG_REQ, omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_REQ_REG_REQ,
ULPD_SOFT_REQ_REG); 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 */ /* Enable OHCI */
writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_OTG_REQ, omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_OTG_REQ,
ULPD_SOFT_REQ_REG); ULPD_SOFT_REQ);
/* USB host clock request if not using OTG */ /* USB host clock request if not using OTG */
writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_REQ, omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_REQ,
ULPD_SOFT_REQ_REG); ULPD_SOFT_REQ);
writel(readl(ULPD_STATUS_REQ_REG) | USB_HOST_DPLL_REQ, omap_writel(omap_readl(ULPD_STATUS_REQ) | USB_HOST_DPLL_REQ,
ULPD_STATUS_REQ_REG); ULPD_STATUS_REQ);
} }
/* Enable 48MHz clock to USB */ /* Enable 48MHz clock to USB */
writel(readl(ULPD_CLOCK_CTRL_REG) | USB_MCLK_EN, omap_writel(omap_readl(ULPD_CLOCK_CTRL) | USB_MCLK_EN,
ULPD_CLOCK_CTRL_REG); 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); 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); MOD_CONF_CTRL_0);
} else { } else {
/* Disable 48MHz clock to USB */ /* Disable 48MHz clock to USB */
writel(readl(ULPD_CLOCK_CTRL_REG) & ~USB_MCLK_EN, omap_writel(omap_readl(ULPD_CLOCK_CTRL) & ~USB_MCLK_EN,
ULPD_CLOCK_CTRL_REG); ULPD_CLOCK_CTRL);
/* FIXME: The DPLL stays on for now */ /* FIXME: The DPLL stays on for now */
} }
...@@ -163,13 +97,19 @@ static int omap_ohci_clock_power(int on) ...@@ -163,13 +97,19 @@ static int omap_ohci_clock_power(int on)
static int omap_ohci_transceiver_power(int on) static int omap_ohci_transceiver_power(int on)
{ {
if (on) { if (on) {
if (omap_is_innovator()) if (machine_is_omap_innovator() && cpu_is_omap1510())
writel(readl(OMAP1510_FPGA_HOST_CTRL) | 0x20, fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) | 0x20,
OMAP1510_FPGA_HOST_CTRL); INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk()) {
/* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */
}
} else { } else {
if (omap_is_innovator()) if (machine_is_omap_innovator() && cpu_is_omap1510())
writel(readl(OMAP1510_FPGA_HOST_CTRL) & ~0x20, fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~0x20,
OMAP1510_FPGA_HOST_CTRL); INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk()) {
/* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */
}
} }
return 0; return 0;
...@@ -181,10 +121,10 @@ static int omap_ohci_transceiver_power(int on) ...@@ -181,10 +121,10 @@ static int omap_ohci_transceiver_power(int on)
static int omap_1510_local_bus_power(int on) static int omap_1510_local_bus_power(int on)
{ {
if (on) { if (on) {
writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL);
udelay(200); udelay(200);
} else { } else {
writel(0, OMAP1510_LB_MMU_CTL); omap_writel(0, OMAP1510_LB_MMU_CTL);
} }
return 0; return 0;
...@@ -193,8 +133,8 @@ static int omap_1510_local_bus_power(int on) ...@@ -193,8 +133,8 @@ static int omap_1510_local_bus_power(int on)
/* /*
* OMAP-1510 specific Local Bus initialization * OMAP-1510 specific Local Bus initialization
* NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
* See also arch/mach-omap/memory.h for __virt_to_bus() and * See also arch/mach-omap/memory.h for __virt_to_dma() and
* __bus_to_virt() which need to match with the physical * __dma_to_virt() which need to match with the physical
* Local Bus address below. * Local Bus address below.
*/ */
static int omap_1510_local_bus_init(void) static int omap_1510_local_bus_init(void)
...@@ -202,125 +142,115 @@ static int omap_1510_local_bus_init(void) ...@@ -202,125 +142,115 @@ static int omap_1510_local_bus_init(void)
unsigned int tlb; unsigned int tlb;
unsigned long lbaddr, physaddr; 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); OMAP1510_LB_CLOCK_DIV);
/* Configure the Local Bus MMU table */ /* Configure the Local Bus MMU table */
for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) { for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) {
lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
physaddr = tlb * 0x00100000 + PHYS_OFFSET; physaddr = tlb * 0x00100000 + PHYS_OFFSET;
writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
OMAP1510_LB_MMU_CAM_L); OMAP1510_LB_MMU_CAM_L);
writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
writel(tlb << 4, OMAP1510_LB_MMU_LCK); omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK);
writel(0x1, OMAP1510_LB_MMU_LD_TLB); omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB);
} }
/* Enable the walking table */ /* 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); udelay(200);
return 0; return 0;
} }
/* #ifdef CONFIG_USB_OTG
* 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 */ static void start_hnp(struct ohci_hcd *ohci)
val |= (1 << 2); /* Disable pulldown on integrated transceiver DM */ {
val |= (1 << 1); /* Disable pulldown on integraded transceiver DP */ 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 */ local_irq_save(flags);
val = 0; ohci->transceiver->state = OTG_STATE_A_SUSPEND;
val &= ~OTG_IDLE_EN; writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
val &= ~DEV_IDLE_EN; OTG_CTRL_REG &= ~OTG_A_BUSREQ;
val &= ~(7 << 16); /* Clear USB0_TRX_MODE */ local_irq_restore(flags);
val |= (3 << 16); /* 0 or 3, 6-wire DAT/SE0, TRM p 15-159 */ }
writel(val, OTG_SYSCON_1);
/* #endif
* 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;
val &= ~0x3f; /* Clear HMC mode */ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
val |= mode; /* Set HMC mode */ {
val &= ~(7 << 16); /* Clear ASE0_BRST */ struct omap_usb_config *config = pdev->dev.platform_data;
val |= (4 << 16); /* Must be 4 */ int need_transceiver = (config->otg != 0);
val |= USBX_SYNCHRO; /* Must be set */
val |= SRP_VBUS;
writel(val, OTG_SYSCON_2);
/* Enable OTG idle */ dev_dbg(&pdev->dev, "starting USB Controller\n");
//writel(readl(OTG_SYSCON_1) | OTG_IDLE_EN, OTG_SYSCON_1);
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) if (cpu_is_omap16xx())
{ ocpi_enable();
printk(KERN_DEBUG __FILE__
": starting OMAP OHCI USB Controller\n");
/* #ifdef CONFIG_ARCH_OMAP_OTG
* Set the HMC mode for the USB ports if (need_transceiver) {
*/ ohci->transceiver = otg_get_transceiver();
#if 0 if (ohci->transceiver) {
/* See note about the Innovator wiring above */ int status = otg_set_host(ohci->transceiver,
if (omap_is_innovator()) &ohci->hcd.self);
hmc_mode = 4; /* 0: function 1: host 2: host */ 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 #endif
if (cpu_is_omap_1610()) if (machine_is_omap_osk()) {
ocpi_enable(); omap_request_gpio(9);
omap_set_gpio_direction(9, 1);
omap_usb_set_hmc_mode(hmc_mode); omap_set_gpio_dataout(9, 1);
}
omap_ohci_clock_power(1); omap_ohci_clock_power(1);
omap_ohci_transceiver_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_power(1);
omap_1510_local_bus_init(); omap_1510_local_bus_init();
} }
if (cpu_is_omap_1610()) /* board init will have already handled HMC and mux setup.
omap_1610_usb_init(hmc_mode); * 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__ dev_dbg(&pdev->dev, "stopping USB Controller\n");
": stopping OMAP OHCI USB Controller\n");
/* /*
* FIXME: Put the USB host controller into reset. * FIXME: Put the USB host controller into reset.
...@@ -336,16 +266,7 @@ static void omap_stop_hc(struct omap_dev *dev) ...@@ -336,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) void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
{
struct usb_hcd *hcd = __hcd;
return usb_hcd_irq(irq, hcd, r);
}
/*-------------------------------------------------------------------------*/
void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *);
/* configure so an HC device and id are always provided */ /* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */ /* always called with process context; sleeping is OK */
...@@ -358,59 +279,71 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *); ...@@ -358,59 +279,71 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *);
* Allocates basic resources for this USB host controller, and * Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it * then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data. * 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, int usb_hcd_omap_probe (const struct hc_driver *driver,
struct usb_hcd **hcd_out, struct platform_device *pdev)
struct omap_dev *dev)
{ {
int retval; int retval;
struct usb_hcd *hcd = 0; struct usb_hcd *hcd = 0;
struct ohci_hcd *ohci;
if (!request_mem_region(dev->res.start, if (pdev->num_resources != 2) {
dev->res.end - dev->res.start + 1, hcd_name)) { printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
dbg("request_mem_region failed"); pdev->num_resources);
return -EBUSY; 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 (); hcd = driver->hcd_alloc ();
if (hcd == NULL){ if (hcd == NULL){
dbg ("hcd_alloc failed"); dev_dbg(&pdev->dev, "hcd_alloc failed\n");
retval = -ENOMEM; retval = -ENOMEM;
goto err1; goto err1;
} }
dev_set_drvdata(&pdev->dev, hcd);
ohci = hcd_to_ohci(hcd);
hcd->driver = (struct hc_driver *) driver; hcd->driver = (struct hc_driver *) driver;
hcd->description = driver->description; hcd->description = driver->description;
hcd->irq = dev->irq[0]; hcd->irq = pdev->resource[1].start;
hcd->regs = dev->mapbase; hcd->regs = (void *)pdev->resource[0].start;
hcd->self.controller = &dev->dev; hcd->self.controller = &pdev->dev;
retval = omap_start_hc(ohci, pdev);
if (retval < 0)
goto err2;
retval = hcd_buffer_create (hcd); retval = hcd_buffer_create (hcd);
if (retval != 0) { if (retval != 0) {
dbg ("pool alloc fail"); dev_dbg(&pdev->dev, "pool alloc fail\n");
goto err1; goto err1;
} }
retval = request_irq (hcd->irq, retval = request_irq (hcd->irq, usb_hcd_irq,
usb_hcd_omap_hcim_irq,
SA_INTERRUPT, hcd->description, hcd); SA_INTERRUPT, hcd->description, hcd);
if (retval != 0) { if (retval != 0) {
dbg("request_irq failed"); dev_dbg(&pdev->dev, "request_irq failed\n");
retval = -EBUSY; retval = -EBUSY;
goto err2; goto err2;
} }
info ("%s (OMAP) at 0x%p, irq %d\n", dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
hcd->description, hcd->regs, hcd->irq);
usb_bus_init (&hcd->self); usb_bus_init (&hcd->self);
hcd->self.op = &usb_hcd_operations; hcd->self.op = &usb_hcd_operations;
hcd->self.hcpriv = (void *) hcd; hcd->self.hcpriv = (void *) hcd;
hcd->self.bus_name = "omap"; hcd->self.bus_name = pdev->dev.bus_id;
hcd->product_desc = "OMAP OHCI"; hcd->product_desc = "OMAP OHCI";
INIT_LIST_HEAD (&hcd->dev_list); INIT_LIST_HEAD (&hcd->dev_list);
...@@ -418,11 +351,10 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, ...@@ -418,11 +351,10 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
if ((retval = driver->start (hcd)) < 0) if ((retval = driver->start (hcd)) < 0)
{ {
usb_hcd_omap_remove(hcd, dev); usb_hcd_omap_remove(hcd, pdev);
return retval; return retval;
} }
*hcd_out = hcd;
return 0; return 0;
err2: err2:
...@@ -430,10 +362,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, ...@@ -430,10 +362,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
if (hcd) if (hcd)
driver->hcd_free(hcd); driver->hcd_free(hcd);
err1: 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; return retval;
} }
...@@ -451,24 +385,27 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, ...@@ -451,24 +385,27 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
* context, normally "rmmod", "apmd", or something similar. * 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)
{ {
void *base; 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 ()) if (in_interrupt ())
BUG (); BUG ();
hcd->state = USB_STATE_QUIESCING; hcd->state = USB_STATE_QUIESCING;
dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); dev_dbg(&pdev->dev, "roothub graceful disconnect\n");
usb_disconnect (&hcd->self.root_hub); usb_disconnect (&hcd->self.root_hub);
hcd->driver->stop (hcd); hcd->driver->stop (hcd);
hcd_buffer_destroy (hcd); hcd_buffer_destroy (hcd);
hcd->state = USB_STATE_HALT; hcd->state = USB_STATE_HALT;
if (machine_is_omap_osk())
omap_free_gpio(9);
free_irq (hcd->irq, hcd); free_irq (hcd->irq, hcd);
usb_deregister_bus (&hcd->self); usb_deregister_bus (&hcd->self);
...@@ -476,9 +413,10 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) ...@@ -476,9 +413,10 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev)
base = hcd->regs; base = hcd->regs;
hcd->driver->hcd_free (hcd); 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);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -486,11 +424,13 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) ...@@ -486,11 +424,13 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev)
static int __devinit static int __devinit
ohci_omap_start (struct usb_hcd *hcd) ohci_omap_start (struct usb_hcd *hcd)
{ {
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret; int ret;
ohci->hcca = dma_alloc_consistent (hcd->self.controller, config = hcd->self.controller->platform_data;
sizeof *ohci->hcca, &ohci->hcca_dma); ohci->hcca = dma_alloc_coherent (hcd->self.controller,
sizeof *ohci->hcca, &ohci->hcca_dma, 0);
if (!ohci->hcca) if (!ohci->hcca)
return -ENOMEM; return -ENOMEM;
...@@ -500,6 +440,10 @@ ohci_omap_start (struct usb_hcd *hcd) ...@@ -500,6 +440,10 @@ ohci_omap_start (struct usb_hcd *hcd)
return ret; return ret;
} }
ohci->regs = hcd->regs; ohci->regs = hcd->regs;
if (config->otg || config->rwc)
writel(OHCI_CTRL_RWC, &ohci->regs->control);
if (hc_reset (ohci) < 0) { if (hc_reset (ohci) < 0) {
ohci_stop (hcd); ohci_stop (hcd);
return -ENODEV; return -ENODEV;
...@@ -510,6 +454,9 @@ ohci_omap_start (struct usb_hcd *hcd) ...@@ -510,6 +454,9 @@ ohci_omap_start (struct usb_hcd *hcd)
ohci_stop (hcd); ohci_stop (hcd);
return -EBUSY; return -EBUSY;
} }
if (ohci->power_budget)
hub_set_power_budget(ohci->hcd.self.root_hub,
ohci->power_budget);
create_debug_files (ohci); create_debug_files (ohci);
#ifdef DEBUG #ifdef DEBUG
...@@ -533,10 +480,6 @@ static const struct hc_driver ohci_omap_hc_driver = { ...@@ -533,10 +480,6 @@ static const struct hc_driver ohci_omap_hc_driver = {
* basic lifecycle operations * basic lifecycle operations
*/ */
.start = ohci_omap_start, .start = ohci_omap_start,
#ifdef CONFIG_PM
/* suspend: ohci_omap_suspend, -- tbd */
/* resume: ohci_omap_resume, -- tbd */
#endif
.stop = ohci_stop, .stop = ohci_stop,
/* /*
...@@ -571,100 +514,125 @@ static const struct hc_driver ohci_omap_hc_driver = { ...@@ -571,100 +514,125 @@ static const struct hc_driver ohci_omap_hc_driver = {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
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; return usb_hcd_omap_probe(&ohci_omap_hc_driver,
int ret; to_platform_device(dev));
}
if (usb_disabled()) static int ohci_hcd_omap_drv_remove(struct device *dev)
return -ENODEV; {
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);
return 0;
}
/*-------------------------------------------------------------------------*/
ret = usb_hcd_omap_probe(&ohci_omap_hc_driver, &hcd, dev); #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
if (ret == 0) /* states match PCI usage, always suspending the root hub except that
omap_set_drvdata(dev, hcd); * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset).
*/
return ret; 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 * Driver definition to register with the OMAP bus
*/ */
static struct omap_driver ohci_hcd_omap_driver = { static struct device_driver ohci_hcd_omap_driver = {
.drv = { .name = "ohci",
.name = OMAP_OHCI_NAME, .bus = &platform_bus_type,
},
.devid = OMAP_OCP_DEVID_USB,
.busid = OMAP_BUS_OCP,
.clocks = 0,
.probe = ohci_hcd_omap_drv_probe, .probe = ohci_hcd_omap_drv_probe,
.remove = ohci_hcd_omap_drv_remove, .remove = ohci_hcd_omap_drv_remove,
}; #if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
.suspend = ohci_omap_suspend,
/* Any dma_mask must be set for OHCI to work */ .resume = ohci_omap_resume,
static u64 omap_dmamask = 0xffffffffUL; #endif
/*
* 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,
},
}; };
static int __init ohci_hcd_omap_init (void) static int __init ohci_hcd_omap_init (void)
{ {
int ret; printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name);
if (usb_disabled())
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)
return -ENODEV; return -ENODEV;
/* Register the device with OMAP bus */ pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
ret = omap_device_register(&ohci_hcd_omap_device); sizeof (struct ed), sizeof (struct td));
if (ret != 0) {
omap_driver_unregister(&ohci_hcd_omap_driver);
return -ENODEV;
}
return ret; return driver_register(&ohci_hcd_omap_driver);
} }
module_param(hmc_mode, int, 0);
static void __exit ohci_hcd_omap_cleanup (void) static void __exit ohci_hcd_omap_cleanup (void)
{ {
omap_device_unregister(&ohci_hcd_omap_device); driver_unregister(&ohci_hcd_omap_driver);
omap_driver_unregister(&ohci_hcd_omap_driver);
} }
module_init (ohci_hcd_omap_init); module_init (ohci_hcd_omap_init);
......
/*
* linux/drivers/usb/host/ohci-omap.h
*
* OMAP OHCI USB controller specific defines
*/
/* OMAP USB OHCI common defines */
#define OMAP_OHCI_NAME "omap-ohci"
#define OMAP_OHCI_BASE 0xfffba000
#define OMAP_OHCI_SIZE 4096
#define HMC_CLEAR (0x3f << 1)
#define APLL_NDPLL_SWITCH 0x0001
#define DPLL_PLL_ENABLE 0x0010
#define DPLL_LOCK 0x0001
#define SOFT_REQ_REG_REQ 0x0001
#define USB_MCLK_EN 0x0010
#define USB_HOST_HHC_UHOST_EN 0x00000200
#define SOFT_USB_OTG_REQ (1 << 8)
#define SOFT_USB_REQ (1 << 3)
#define STATUS_REQ_REG 0xfffe0840
#define USB_HOST_DPLL_REQ (1 << 8)
#define SOFT_DPLL_REQ (1 << 0)
/* OMAP-1510 USB OHCI defines */
#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
/* OMAP-1610 USB OHCI defines */
#define USB_TRANSCEIVER_CTRL 0xfffe1064
#define OTG_REV 0xfffb0400
#define OTG_SYSCON_1 0xfffb0404
#define OTG_IDLE_EN (1 << 15)
#define DEV_IDLE_EN (1 << 13)
#define OTG_SYSCON_2 0xfffb0408
#define OTG_CTRL 0xfffb040c
#define OTG_IRQ_EN 0xfffb0410
#define OTG_IRQ_SRC 0xfffb0414
#define OTG_EN (1 << 31)
#define USBX_SYNCHRO (1 << 30)
#define SRP_VBUS (1 << 12)
#define OTG_PADEN (1 << 10)
#define HMC_PADEN (1 << 9)
#define UHOST_EN (1 << 8)
/* Hardware specific defines */
#define OMAP1510_FPGA_HOST_CTRL 0xe800020c
...@@ -359,6 +359,13 @@ struct ohci_hcd { ...@@ -359,6 +359,13 @@ struct ohci_hcd {
struct ed *ed_controltail; /* last in ctrl list */ struct ed *ed_controltail; /* last in ctrl list */
struct ed *periodic [NUM_INTS]; /* shadow int_table */ struct ed *periodic [NUM_INTS]; /* shadow int_table */
/*
* OTG controllers and transceivers need software interaction;
* other external transceivers should be software-transparent
*/
struct otg_transceiver *transceiver;
unsigned power_budget;
/* /*
* memory management for queue data structures * memory management for queue data structures
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment