USB: Add MUSB and TUSB support

This patch adds support for MUSB and TUSB controllers
integrated into omap2430 and davinci. It also adds support
for external tusb6010 controller.

Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Felipe Balbi 2008-07-24 12:27:36 +03:00 committed by Greg Kroah-Hartman
parent f331e40ee8
commit 550a7375fe
30 changed files with 15764 additions and 0 deletions

View File

@ -2928,6 +2928,12 @@ M: jirislaby@gmail.com
L: linux-kernel@vger.kernel.org
S: Maintained
MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
P: Felipe Balbi
M: felipe.balbi@nokia.com
L: linux-usb@vger.kernel.org
S: Maintained
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
P: Andrew Gallatin
M: gallatin@myri.com

View File

@ -57,6 +57,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/

View File

@ -99,6 +99,8 @@ source "drivers/usb/mon/Kconfig"
source "drivers/usb/host/Kconfig"
source "drivers/usb/musb/Kconfig"
source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig"

View File

@ -284,6 +284,16 @@ config USB_LH7A40X
default USB_GADGET
select USB_GADGET_SELECTED
# built in ../musb along with host support
config USB_GADGET_MUSB_HDRC
boolean "Inventra HDRC USB Peripheral (TI, ...)"
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
select USB_GADGET_DUALSPEED
select USB_GADGET_SELECTED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010.
config USB_GADGET_OMAP
boolean "OMAP USB Device Controller"
depends on ARCH_OMAP

176
drivers/usb/musb/Kconfig Normal file
View File

@ -0,0 +1,176 @@
#
# USB Dual Role (OTG-ready) Controller Drivers
# for silicon based on Mentor Graphics INVENTRA designs
#
comment "Enable Host or Gadget support to see Inventra options"
depends on !USB && USB_GADGET=n
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
depends on (USB || USB_GADGET) && HAVE_CLK
select TWL4030_USB if MACH_OMAP_3430SDP
tristate 'Inventra Highspeed Dual Role Controller (TI, ...)'
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
configure options to match your silicon and the board
it's being used with, including the USB peripheral role,
or the USB host role, or both.
Texas Instruments parts using this IP include DaVinci 644x,
OMAP 243x, OMAP 343x, and TUSB 6010.
If you do not know what this is, please say N.
To compile this driver as a module, choose M here; the
module will be called "musb_hdrc".
config USB_MUSB_SOC
boolean
depends on USB_MUSB_HDRC
default y if ARCH_DAVINCI
default y if ARCH_OMAP2430
default y if ARCH_OMAP34XX
help
Use a static <asm/arch/hdrc_cnf.h> file to describe how the
controller is configured (endpoints, mechanisms, etc) on the
current iteration of a given system-on-chip.
comment "DaVinci 644x USB support"
depends on USB_MUSB_HDRC && ARCH_DAVINCI
comment "OMAP 243x high speed USB support"
depends on USB_MUSB_HDRC && ARCH_OMAP2430
comment "OMAP 343x high speed USB support"
depends on USB_MUSB_HDRC && ARCH_OMAP34XX
config USB_TUSB6010
boolean "TUSB 6010 support"
depends on USB_MUSB_HDRC && !USB_MUSB_SOC
default y
help
The TUSB 6010 chip, from Texas Instruments, connects a discrete
HDRC core using a 16-bit parallel bus (NOR flash style) or VLYNQ
(a high speed serial link). It can use system-specific external
DMA controllers.
choice
prompt "Driver Mode"
depends on USB_MUSB_HDRC
help
Dual-Role devices can support both host and peripheral roles,
as well as a the special "OTG Device" role which can switch
between both roles as needed.
# use USB_MUSB_HDRC_HCD not USB_MUSB_HOST to #ifdef host side support;
# OTG needs both roles, not just USB_MUSB_HOST.
config USB_MUSB_HOST
depends on USB
bool "USB Host"
help
Say Y here if your system supports the USB host role.
If it has a USB "A" (rectangular), "Mini-A" (uncommon),
or "Mini-AB" connector, it supports the host role.
(With a "Mini-AB" connector, you should enable USB OTG.)
# use USB_GADGET_MUSB_HDRC not USB_MUSB_PERIPHERAL to #ifdef peripheral
# side support ... OTG needs both roles
config USB_MUSB_PERIPHERAL
depends on USB_GADGET
bool "USB Peripheral (gadget stack)"
select USB_GADGET_MUSB_HDRC
help
Say Y here if your system supports the USB peripheral role.
If it has a USB "B" (squarish), "Mini-B", or "Mini-AB"
connector, it supports the peripheral role.
(With a "Mini-AB" connector, you should enable USB OTG.)
config USB_MUSB_OTG
depends on USB && USB_GADGET && PM && EXPERIMENTAL
bool "Both host and peripheral: USB OTG (On The Go) Device"
select USB_GADGET_MUSB_HDRC
select USB_OTG
help
The most notable feature of USB OTG is support for a
"Dual-Role" device, which can act as either a device
or a host. The initial role choice can be changed
later, when two dual-role devices talk to each other.
At this writing, the OTG support in this driver is incomplete,
omitting the mandatory HNP or SRP protocols. However, some
of the cable based role switching works. (That is, grounding
the ID pin switches the controller to host mode, while leaving
it floating leaves it in peripheral mode.)
Select this if your system has a Mini-AB connector, or
to simplify certain kinds of configuration.
To implement your OTG Targeted Peripherals List (TPL), enable
USB_OTG_WHITELIST and update "drivers/usb/core/otg_whitelist.h"
to match your requirements.
endchoice
# enable peripheral support (including with OTG)
config USB_GADGET_MUSB_HDRC
bool
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
# default y
# select USB_GADGET_DUALSPEED
# select USB_GADGET_SELECTED
# enables host support (including with OTG)
config USB_MUSB_HDRC_HCD
bool
depends on USB_MUSB_HDRC && (USB_MUSB_HOST || USB_MUSB_OTG)
select USB_OTG if USB_GADGET_MUSB_HDRC
default y
config MUSB_PIO_ONLY
bool 'Disable DMA (always use PIO)'
depends on USB_MUSB_HDRC
default y if USB_TUSB6010
help
All data is copied between memory and FIFO by the CPU.
DMA controllers are ignored.
Do not select 'n' here unless DMA support for your SOC or board
is unavailable (or unstable). When DMA is enabled at compile time,
you can still disable it at run time using the "use_dma=n" module
parameter.
config USB_INVENTRA_DMA
bool
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
default ARCH_OMAP2430 || ARCH_OMAP34XX
help
Enable DMA transfers using Mentor's engine.
config USB_TI_CPPI_DMA
bool
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
default ARCH_DAVINCI
help
Enable DMA transfers when TI CPPI DMA is available.
config USB_TUSB_OMAP_DMA
bool
depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY
depends on USB_TUSB6010
depends on ARCH_OMAP
default y
help
Enable DMA transfers on TUSB 6010 when OMAP DMA is available.
config USB_MUSB_LOGLEVEL
depends on USB_MUSB_HDRC
int 'Logging Level (0 - none / 3 - annoying / ... )'
default 0
help
Set the logging level. 0 disables the debugging altogether,
although when USB_DEBUG is set the value is at least 1.
Starting at level 3, per-transfer (urb, usb_request, packet,
or dma transfer) tracing may kick in.

86
drivers/usb/musb/Makefile Normal file
View File

@ -0,0 +1,86 @@
#
# for USB OTG silicon based on Mentor Graphics INVENTRA designs
#
musb_hdrc-objs := musb_core.o
obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
ifeq ($(CONFIG_ARCH_DAVINCI),y)
musb_hdrc-objs += davinci.o
endif
ifeq ($(CONFIG_USB_TUSB6010),y)
musb_hdrc-objs += tusb6010.o
endif
ifeq ($(CONFIG_ARCH_OMAP2430),y)
musb_hdrc-objs += omap2430.o
endif
ifeq ($(CONFIG_ARCH_OMAP3430),y)
musb_hdrc-objs += omap2430.o
endif
ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y)
musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o
endif
ifeq ($(CONFIG_USB_MUSB_HDRC_HCD),y)
musb_hdrc-objs += musb_virthub.o musb_host.o
endif
# the kconfig must guarantee that only one of the
# possible I/O schemes will be enabled at a time ...
# PIO only, or DMA (several potential schemes).
# though PIO is always there to back up DMA, and for ep0
ifneq ($(CONFIG_MUSB_PIO_ONLY),y)
ifeq ($(CONFIG_USB_INVENTRA_DMA),y)
musb_hdrc-objs += musbhsdma.o
else
ifeq ($(CONFIG_USB_TI_CPPI_DMA),y)
musb_hdrc-objs += cppi_dma.o
else
ifeq ($(CONFIG_USB_TUSB_OMAP_DMA),y)
musb_hdrc-objs += tusb6010_omap.o
endif
endif
endif
endif
################################################################################
# FIXME remove all these extra "-DMUSB_* things, stick to CONFIG_*
ifeq ($(CONFIG_USB_INVENTRA_MUSB_HAS_AHB_ID),y)
EXTRA_CFLAGS += -DMUSB_AHB_ID
endif
# Debugging
MUSB_DEBUG:=$(CONFIG_USB_MUSB_LOGLEVEL)
ifeq ("$(strip $(MUSB_DEBUG))","")
ifdef CONFIG_USB_DEBUG
MUSB_DEBUG:=1
else
MUSB_DEBUG:=0
endif
endif
ifneq ($(MUSB_DEBUG),0)
EXTRA_CFLAGS += -DDEBUG
ifeq ($(CONFIG_PROC_FS),y)
musb_hdrc-objs += musb_procfs.o
endif
endif
EXTRA_CFLAGS += -DMUSB_DEBUG=$(MUSB_DEBUG)

1540
drivers/usb/musb/cppi_dma.c Normal file

File diff suppressed because it is too large Load Diff

133
drivers/usb/musb/cppi_dma.h Normal file
View File

@ -0,0 +1,133 @@
/* Copyright (C) 2005-2006 by Texas Instruments */
#ifndef _CPPI_DMA_H_
#define _CPPI_DMA_H_
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/dmapool.h>
#include "musb_dma.h"
#include "musb_core.h"
/* FIXME fully isolate CPPI from DaVinci ... the "CPPI generic" registers
* would seem to be shared with the TUSB6020 (over VLYNQ).
*/
#include "davinci.h"
/* CPPI RX/TX state RAM */
struct cppi_tx_stateram {
u32 tx_head; /* "DMA packet" head descriptor */
u32 tx_buf;
u32 tx_current; /* current descriptor */
u32 tx_buf_current;
u32 tx_info; /* flags, remaining buflen */
u32 tx_rem_len;
u32 tx_dummy; /* unused */
u32 tx_complete;
};
struct cppi_rx_stateram {
u32 rx_skipbytes;
u32 rx_head;
u32 rx_sop; /* "DMA packet" head descriptor */
u32 rx_current; /* current descriptor */
u32 rx_buf_current;
u32 rx_len_len;
u32 rx_cnt_cnt;
u32 rx_complete;
};
/* hw_options bits in CPPI buffer descriptors */
#define CPPI_SOP_SET ((u32)(1 << 31))
#define CPPI_EOP_SET ((u32)(1 << 30))
#define CPPI_OWN_SET ((u32)(1 << 29)) /* owned by cppi */
#define CPPI_EOQ_MASK ((u32)(1 << 28))
#define CPPI_ZERO_SET ((u32)(1 << 23)) /* rx saw zlp; tx issues one */
#define CPPI_RXABT_MASK ((u32)(1 << 19)) /* need more rx buffers */
#define CPPI_RECV_PKTLEN_MASK 0xFFFF
#define CPPI_BUFFER_LEN_MASK 0xFFFF
#define CPPI_TEAR_READY ((u32)(1 << 31))
/* CPPI data structure definitions */
#define CPPI_DESCRIPTOR_ALIGN 16 /* bytes; 5-dec docs say 4-byte align */
struct cppi_descriptor {
/* hardware overlay */
u32 hw_next; /* next buffer descriptor Pointer */
u32 hw_bufp; /* i/o buffer pointer */
u32 hw_off_len; /* buffer_offset16, buffer_length16 */
u32 hw_options; /* flags: SOP, EOP etc*/
struct cppi_descriptor *next;
dma_addr_t dma; /* address of this descriptor */
u32 buflen; /* for RX: original buffer length */
} __attribute__ ((aligned(CPPI_DESCRIPTOR_ALIGN)));
struct cppi;
/* CPPI Channel Control structure */
struct cppi_channel {
struct dma_channel channel;
/* back pointer to the DMA controller structure */
struct cppi *controller;
/* which direction of which endpoint? */
struct musb_hw_ep *hw_ep;
bool transmit;
u8 index;
/* DMA modes: RNDIS or "transparent" */
u8 is_rndis;
/* book keeping for current transfer request */
dma_addr_t buf_dma;
u32 buf_len;
u32 maxpacket;
u32 offset; /* dma requested */
void __iomem *state_ram; /* CPPI state */
struct cppi_descriptor *freelist;
/* BD management fields */
struct cppi_descriptor *head;
struct cppi_descriptor *tail;
struct cppi_descriptor *last_processed;
/* use tx_complete in host role to track endpoints waiting for
* FIFONOTEMPTY to clear.
*/
struct list_head tx_complete;
};
/* CPPI DMA controller object */
struct cppi {
struct dma_controller controller;
struct musb *musb;
void __iomem *mregs; /* Mentor regs */
void __iomem *tibase; /* TI/CPPI regs */
struct cppi_channel tx[MUSB_C_NUM_EPT - 1];
struct cppi_channel rx[MUSB_C_NUM_EPR - 1];
struct dma_pool *pool;
struct list_head tx_complete;
};
/* irq handling hook */
extern void cppi_completion(struct musb *, u32 rx, u32 tx);
#endif /* end of ifndef _CPPI_DMA_H_ */

462
drivers/usb/musb/davinci.c Normal file
View File

@ -0,0 +1,462 @@
/*
* Copyright (C) 2005-2006 by Texas Instruments
*
* This file is part of the Inventra Controller Driver for Linux.
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*
* The Inventra Controller Driver for Linux is distributed in
* the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with The Inventra Controller Driver for Linux ; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/memory.h>
#include <asm/arch/gpio.h>
#include <asm/mach-types.h>
#include "musb_core.h"
#ifdef CONFIG_MACH_DAVINCI_EVM
#include <asm/arch/i2c-client.h>
#endif
#include "davinci.h"
#include "cppi_dma.h"
/* REVISIT (PM) we should be able to keep the PHY in low power mode most
* of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0
* and, when in host mode, autosuspending idle root ports... PHYPLLON
* (overriding SUSPENDM?) then likely needs to stay off.
*/
static inline void phy_on(void)
{
/* start the on-chip PHY and its PLL */
__raw_writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON,
(void __force __iomem *) IO_ADDRESS(USBPHY_CTL_PADDR));
while ((__raw_readl((void __force __iomem *)
IO_ADDRESS(USBPHY_CTL_PADDR))
& USBPHY_PHYCLKGD) == 0)
cpu_relax();
}
static inline void phy_off(void)
{
/* powerdown the on-chip PHY and its oscillator */
__raw_writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, (void __force __iomem *)
IO_ADDRESS(USBPHY_CTL_PADDR));
}
static int dma_off = 1;
void musb_platform_enable(struct musb *musb)
{
u32 tmp, old, val;
/* workaround: setup irqs through both register sets */
tmp = (musb->epmask & DAVINCI_USB_TX_ENDPTS_MASK)
<< DAVINCI_USB_TXINT_SHIFT;
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
old = tmp;
tmp = (musb->epmask & (0xfffe & DAVINCI_USB_RX_ENDPTS_MASK))
<< DAVINCI_USB_RXINT_SHIFT;
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
tmp |= old;
val = ~MUSB_INTR_SOF;
tmp |= ((val & 0x01ff) << DAVINCI_USB_USBINT_SHIFT);
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_SET_REG, tmp);
if (is_dma_capable() && !dma_off)
printk(KERN_WARNING "%s %s: dma not reactivated\n",
__FILE__, __func__);
else
dma_off = 0;
/* force a DRVVBUS irq so we can start polling for ID change */
if (is_otg_enabled(musb))
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);
}
/*
* Disable the HDRC and flush interrupts
*/
void musb_platform_disable(struct musb *musb)
{
/* because we don't set CTRLR.UINT, "important" to:
* - not read/write INTRUSB/INTRUSBE
* - (except during initial setup, as workaround)
* - use INTSETR/INTCLRR instead
*/
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_MASK_CLR_REG,
DAVINCI_USB_USBINT_MASK
| DAVINCI_USB_TXINT_MASK
| DAVINCI_USB_RXINT_MASK);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_writel(musb->ctrl_base, DAVINCI_USB_EOI_REG, 0);
if (is_dma_capable() && !dma_off)
WARNING("dma still active\n");
}
/* REVISIT it's not clear whether DaVinci can support full OTG. */
static int vbus_state = -1;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
#define portstate(stmt) stmt
#else
#define portstate(stmt)
#endif
/* VBUS SWITCHING IS BOARD-SPECIFIC */
#ifdef CONFIG_MACH_DAVINCI_EVM
#ifndef CONFIG_MACH_DAVINCI_EVM_OTG
/* I2C operations are always synchronous, and require a task context.
* With unloaded systems, using the shared workqueue seems to suffice
* to satisfy the 100msec A_WAIT_VRISE timeout...
*/
static void evm_deferred_drvvbus(struct work_struct *ignored)
{
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state);
vbus_state = !vbus_state;
}
static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus);
#endif /* modified board */
#endif /* EVM */
static void davinci_source_power(struct musb *musb, int is_on, int immediate)
{
if (is_on)
is_on = 1;
if (vbus_state == is_on)
return;
vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */
#ifdef CONFIG_MACH_DAVINCI_EVM
if (machine_is_davinci_evm()) {
#ifdef CONFIG_MACH_DAVINCI_EVM_OTG
/* modified EVM board switching VBUS with GPIO(6) not I2C
* NOTE: PINMUX0.RGB888 (bit23) must be clear
*/
if (is_on)
gpio_set(GPIO(6));
else
gpio_clear(GPIO(6));
immediate = 1;
#else
if (immediate)
davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on);
else
schedule_work(&evm_vbus_work);
#endif
}
#endif
if (immediate)
vbus_state = is_on;
}
static void davinci_set_vbus(struct musb *musb, int is_on)
{
WARN_ON(is_on && is_peripheral_active(musb));
davinci_source_power(musb, is_on, 0);
}
#define POLL_SECONDS 2
static struct timer_list otg_workaround;
static void otg_timer(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
void __iomem *mregs = musb->mregs;
u8 devctl;
unsigned long flags;
/* We poll because DaVinci's won't expose several OTG-critical
* status change events (from the transceiver) otherwise.
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb));
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv.state) {
case OTG_STATE_A_WAIT_VFALL:
/* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
* seems to mis-handle session "start" otherwise (or in our
* case "recover"), in routine "VBUS was valid by the time
* VBUSERR got reported during enumeration" cases.
*/
if (devctl & MUSB_DEVCTL_VBUS) {
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
break;
}
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
break;
case OTG_STATE_B_IDLE:
if (!is_peripheral_enabled(musb))
break;
/* There's no ID-changed IRQ, so we have no good way to tell
* when to switch to the A-Default state machine (by setting
* the DEVCTL.SESSION flag).
*
* Workaround: whenever we're in B_IDLE, try setting the
* session flag every few seconds. If it works, ID was
* grounded and we're now in the A-Default state machine.
*
* NOTE setting the session flag is _supposed_ to trigger
* SRP, but clearly it doesn't.
*/
musb_writeb(mregs, MUSB_DEVCTL,
devctl | MUSB_DEVCTL_SESSION);
devctl = musb_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
else
musb->xceiv.state = OTG_STATE_A_IDLE;
break;
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static irqreturn_t davinci_interrupt(int irq, void *__hci)
{
unsigned long flags;
irqreturn_t retval = IRQ_NONE;
struct musb *musb = __hci;
void __iomem *tibase = musb->ctrl_base;
u32 tmp;
spin_lock_irqsave(&musb->lock, flags);
/* NOTE: DaVinci shadows the Mentor IRQs. Don't manage them through
* the Mentor registers (except for setup), use the TI ones and EOI.
*
* Docs describe irq "vector" registers asociated with the CPPI and
* USB EOI registers. These hold a bitmask corresponding to the
* current IRQ, not an irq handler address. Would using those bits
* resolve some of the races observed in this dispatch code??
*/
/* CPPI interrupts share the same IRQ line, but have their own
* mask, state, "vector", and EOI registers.
*/
if (is_cppi_enabled()) {
u32 cppi_tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
u32 cppi_rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
if (cppi_tx || cppi_rx) {
DBG(4, "CPPI IRQ t%x r%x\n", cppi_tx, cppi_rx);
cppi_completion(musb, cppi_rx, cppi_tx);
retval = IRQ_HANDLED;
}
}
/* ack and handle non-CPPI interrupts */
tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);
musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp);
DBG(4, "IRQ %08x\n", tmp);
musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)
>> DAVINCI_USB_RXINT_SHIFT;
musb->int_tx = (tmp & DAVINCI_USB_TXINT_MASK)
>> DAVINCI_USB_TXINT_SHIFT;
musb->int_usb = (tmp & DAVINCI_USB_USBINT_MASK)
>> DAVINCI_USB_USBINT_SHIFT;
/* DRVVBUS irqs are the only proxy we have (a very poor one!) for
* DaVinci's missing ID change IRQ. We need an ID change IRQ to
* switch appropriately between halves of the OTG state machine.
* Managing DEVCTL.SESSION per Mentor docs requires we know its
* value, but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
*/
if (tmp & (DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT)) {
int drvvbus = musb_readl(tibase, DAVINCI_USB_STAT_REG);
void __iomem *mregs = musb->mregs;
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err = musb->int_usb & MUSB_INTR_VBUSERROR;
err = is_host_enabled(musb)
&& (musb->int_usb & MUSB_INTR_VBUSERROR);
if (err) {
/* The Mentor core doesn't debounce VBUS as needed
* to cope with device connect current spikes. This
* means it's not uncommon for bus-powered devices
* to get VBUS errors during enumeration.
*
* This is a workaround, but newer RTL from Mentor
* seems to allow a better one: "re"starting sessions
* without waiting (on EVM, a **long** time) for VBUS
* to stop registering in devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv.state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (is_host_enabled(musb) && drvvbus) {
musb->is_active = 1;
MUSB_HST_MODE(musb);
musb->xceiv.default_a = 1;
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
musb->xceiv.default_a = 0;
musb->xceiv.state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
/* NOTE: this must complete poweron within 100 msec */
davinci_source_power(musb, drvvbus, 0);
DBG(2, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
otg_state_string(musb),
err ? " ERROR" : "",
devctl);
retval = IRQ_HANDLED;
}
if (musb->int_tx || musb->int_rx || musb->int_usb)
retval |= musb_interrupt(musb);
/* irq stays asserted until EOI is written */
musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
/* poll for ID change */
if (is_otg_enabled(musb)
&& musb->xceiv.state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
/* REVISIT we sometimes get unhandled IRQs
* (e.g. ep0). not clear why...
*/
if (retval != IRQ_HANDLED)
DBG(5, "unhandled? %08x\n", tmp);
return IRQ_HANDLED;
}
int __init musb_platform_init(struct musb *musb)
{
void __iomem *tibase = musb->ctrl_base;
u32 revision;
musb->mregs += DAVINCI_BASE_OFFSET;
#if 0
/* REVISIT there's something odd about clocking, this
* didn't appear do the job ...
*/
musb->clock = clk_get(pDevice, "usb");
if (IS_ERR(musb->clock))
return PTR_ERR(musb->clock);
status = clk_enable(musb->clock);
if (status < 0)
return -ENODEV;
#endif
/* returns zero if e.g. not clocked */
revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);
if (revision == 0)
return -ENODEV;
if (is_host_enabled(musb))
setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
musb->board_set_vbus = davinci_set_vbus;
davinci_source_power(musb, 0, 1);
/* reset the controller */
musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
/* start the on-chip PHY and its PLL */
phy_on();
msleep(5);
/* NOTE: irqs are in mixed mode, not bypass to pure-musb */
pr_debug("DaVinci OTG revision %08x phy %03x control %02x\n",
revision, __raw_readl((void __force __iomem *)
IO_ADDRESS(USBPHY_CTL_PADDR)),
musb_readb(tibase, DAVINCI_USB_CTRL_REG));
musb->isr = davinci_interrupt;
return 0;
}
int musb_platform_exit(struct musb *musb)
{
if (is_host_enabled(musb))
del_timer_sync(&otg_workaround);
davinci_source_power(musb, 0 /*off*/, 1);
/* delay, to avoid problems with module reload */
if (is_host_enabled(musb) && musb->xceiv.default_a) {
int maxdelay = 30;
u8 devctl, warn = 0;
/* if there's no peripheral connected, this can take a
* long time to fall, especially on EVM with huge C133.
*/
do {
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (!(devctl & MUSB_DEVCTL_VBUS))
break;
if ((devctl & MUSB_DEVCTL_VBUS) != warn) {
warn = devctl & MUSB_DEVCTL_VBUS;
DBG(1, "VBUS %d\n",
warn >> MUSB_DEVCTL_VBUS_SHIFT);
}
msleep(1000);
maxdelay--;
} while (maxdelay > 0);
/* in OTG mode, another host might be connected */
if (devctl & MUSB_DEVCTL_VBUS)
DBG(1, "VBUS off timeout (devctl %02x)\n", devctl);
}
phy_off();
return 0;
}

100
drivers/usb/musb/davinci.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2005-2006 by Texas Instruments
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*/
#ifndef __MUSB_HDRDF_H__
#define __MUSB_HDRDF_H__
/*
* DaVinci-specific definitions
*/
/* Integrated highspeed/otg PHY */
#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34)
#define USBPHY_PHYCLKGD (1 << 8)
#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */
#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */
#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */
#define USBPHY_CLKO1SEL (1 << 3)
#define USBPHY_OSCPDWN (1 << 2)
#define USBPHY_PHYPDWN (1 << 0)
/* For now include usb OTG module registers here */
#define DAVINCI_USB_VERSION_REG 0x00
#define DAVINCI_USB_CTRL_REG 0x04
#define DAVINCI_USB_STAT_REG 0x08
#define DAVINCI_RNDIS_REG 0x10
#define DAVINCI_AUTOREQ_REG 0x14
#define DAVINCI_USB_INT_SOURCE_REG 0x20
#define DAVINCI_USB_INT_SET_REG 0x24
#define DAVINCI_USB_INT_SRC_CLR_REG 0x28
#define DAVINCI_USB_INT_MASK_REG 0x2c
#define DAVINCI_USB_INT_MASK_SET_REG 0x30
#define DAVINCI_USB_INT_MASK_CLR_REG 0x34
#define DAVINCI_USB_INT_SRC_MASKED_REG 0x38
#define DAVINCI_USB_EOI_REG 0x3c
#define DAVINCI_USB_EOI_INTVEC 0x40
/* BEGIN CPPI-generic (?) */
/* CPPI related registers */
#define DAVINCI_TXCPPI_CTRL_REG 0x80
#define DAVINCI_TXCPPI_TEAR_REG 0x84
#define DAVINCI_CPPI_EOI_REG 0x88
#define DAVINCI_CPPI_INTVEC_REG 0x8c
#define DAVINCI_TXCPPI_MASKED_REG 0x90
#define DAVINCI_TXCPPI_RAW_REG 0x94
#define DAVINCI_TXCPPI_INTENAB_REG 0x98
#define DAVINCI_TXCPPI_INTCLR_REG 0x9c
#define DAVINCI_RXCPPI_CTRL_REG 0xC0
#define DAVINCI_RXCPPI_MASKED_REG 0xD0
#define DAVINCI_RXCPPI_RAW_REG 0xD4
#define DAVINCI_RXCPPI_INTENAB_REG 0xD8
#define DAVINCI_RXCPPI_INTCLR_REG 0xDC
#define DAVINCI_RXCPPI_BUFCNT0_REG 0xE0
#define DAVINCI_RXCPPI_BUFCNT1_REG 0xE4
#define DAVINCI_RXCPPI_BUFCNT2_REG 0xE8
#define DAVINCI_RXCPPI_BUFCNT3_REG 0xEC
/* CPPI state RAM entries */
#define DAVINCI_CPPI_STATERAM_BASE_OFFSET 0x100
#define DAVINCI_TXCPPI_STATERAM_OFFSET(chnum) \
(DAVINCI_CPPI_STATERAM_BASE_OFFSET + ((chnum) * 0x40))
#define DAVINCI_RXCPPI_STATERAM_OFFSET(chnum) \
(DAVINCI_CPPI_STATERAM_BASE_OFFSET + 0x20 + ((chnum) * 0x40))
/* CPPI masks */
#define DAVINCI_DMA_CTRL_ENABLE 1
#define DAVINCI_DMA_CTRL_DISABLE 0
#define DAVINCI_DMA_ALL_CHANNELS_ENABLE 0xF
#define DAVINCI_DMA_ALL_CHANNELS_DISABLE 0xF
/* END CPPI-generic (?) */
#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
#define DAVINCI_USB_USBINT_SHIFT 16
#define DAVINCI_USB_TXINT_SHIFT 0
#define DAVINCI_USB_RXINT_SHIFT 8
#define DAVINCI_INTR_DRVVBUS 0x0100
#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
#define DAVINCI_USB_TXINT_MASK \
(DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT)
#define DAVINCI_USB_RXINT_MASK \
(DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT)
#define DAVINCI_BASE_OFFSET 0x400
#endif /* __MUSB_HDRDF_H__ */

2266
drivers/usb/musb/musb_core.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,517 @@
/*
* MUSB OTG driver defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_CORE_H__
#define __MUSB_CORE_H__
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
#include <linux/usb/musb.h>
struct musb;
struct musb_hw_ep;
struct musb_ep;
#include "musb_debug.h"
#include "musb_dma.h"
#ifdef CONFIG_USB_MUSB_SOC
/*
* Get core configuration from a header converted (by cfg_conv)
* from the Verilog config file generated by the core config utility
*
* For now we assume that header is provided along with other
* arch-specific files. Discrete chips will need a build tweak.
* So will using AHB IDs from silicon that provides them.
*/
#include <asm/arch/hdrc_cnf.h>
#endif
#include "musb_io.h"
#include "musb_regs.h"
#include "musb_gadget.h"
#include "../core/hcd.h"
#include "musb_host.h"
#ifdef CONFIG_USB_MUSB_OTG
#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
/* NOTE: otg and peripheral-only state machines start at B_IDLE.
* OTG or host-only go to A_IDLE when ID is sensed.
*/
#define is_peripheral_active(m) (!(m)->is_host)
#define is_host_active(m) ((m)->is_host)
#else
#define is_peripheral_enabled(musb) is_peripheral_capable()
#define is_host_enabled(musb) is_host_capable()
#define is_otg_enabled(musb) 0
#define is_peripheral_active(musb) is_peripheral_capable()
#define is_host_active(musb) is_host_capable()
#endif
#if defined(CONFIG_USB_MUSB_OTG) || defined(CONFIG_USB_MUSB_PERIPHERAL)
/* for some reason, the "select USB_GADGET_MUSB_HDRC" doesn't always
* override that choice selection (often USB_GADGET_DUMMY_HCD).
*/
#ifndef CONFIG_USB_GADGET_MUSB_HDRC
#error bogus Kconfig output ... select CONFIG_USB_GADGET_MUSB_HDRC
#endif
#endif /* need MUSB gadget selection */
#ifdef CONFIG_PROC_FS
#include <linux/fs.h>
#define MUSB_CONFIG_PROC_FS
#endif
/****************************** PERIPHERAL ROLE *****************************/
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
#define is_peripheral_capable() (1)
extern irqreturn_t musb_g_ep0_irq(struct musb *);
extern void musb_g_tx(struct musb *, u8);
extern void musb_g_rx(struct musb *, u8);
extern void musb_g_reset(struct musb *);
extern void musb_g_suspend(struct musb *);
extern void musb_g_resume(struct musb *);
extern void musb_g_wakeup(struct musb *);
extern void musb_g_disconnect(struct musb *);
#else
#define is_peripheral_capable() (0)
static inline irqreturn_t musb_g_ep0_irq(struct musb *m) { return IRQ_NONE; }
static inline void musb_g_reset(struct musb *m) {}
static inline void musb_g_suspend(struct musb *m) {}
static inline void musb_g_resume(struct musb *m) {}
static inline void musb_g_wakeup(struct musb *m) {}
static inline void musb_g_disconnect(struct musb *m) {}
#endif
/****************************** HOST ROLE ***********************************/
#ifdef CONFIG_USB_MUSB_HDRC_HCD
#define is_host_capable() (1)
extern irqreturn_t musb_h_ep0_irq(struct musb *);
extern void musb_host_tx(struct musb *, u8);
extern void musb_host_rx(struct musb *, u8);
#else
#define is_host_capable() (0)
static inline irqreturn_t musb_h_ep0_irq(struct musb *m) { return IRQ_NONE; }
static inline void musb_host_tx(struct musb *m, u8 e) {}
static inline void musb_host_rx(struct musb *m, u8 e) {}
#endif
/****************************** CONSTANTS ********************************/
#ifndef MUSB_C_NUM_EPS
#define MUSB_C_NUM_EPS ((u8)16)
#endif
#ifndef MUSB_MAX_END0_PACKET
#define MUSB_MAX_END0_PACKET ((u16)MUSB_EP0_FIFOSIZE)
#endif
/* host side ep0 states */
enum musb_h_ep0_state {
MUSB_EP0_IDLE,
MUSB_EP0_START, /* expect ack of setup */
MUSB_EP0_IN, /* expect IN DATA */
MUSB_EP0_OUT, /* expect ack of OUT DATA */
MUSB_EP0_STATUS, /* expect ack of STATUS */
} __attribute__ ((packed));
/* peripheral side ep0 states */
enum musb_g_ep0_state {
MUSB_EP0_STAGE_SETUP, /* idle, waiting for setup */
MUSB_EP0_STAGE_TX, /* IN data */
MUSB_EP0_STAGE_RX, /* OUT data */
MUSB_EP0_STAGE_STATUSIN, /* (after OUT data) */
MUSB_EP0_STAGE_STATUSOUT, /* (after IN data) */
MUSB_EP0_STAGE_ACKWAIT, /* after zlp, before statusin */
} __attribute__ ((packed));
/* OTG protocol constants */
#define OTG_TIME_A_WAIT_VRISE 100 /* msec (max) */
#define OTG_TIME_A_WAIT_BCON 0 /* 0=infinite; min 1000 msec */
#define OTG_TIME_A_IDLE_BDIS 200 /* msec (min) */
/*************************** REGISTER ACCESS ********************************/
/* Endpoint registers (other than dynfifo setup) can be accessed either
* directly with the "flat" model, or after setting up an index register.
*/
#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \
|| defined(CONFIG_ARCH_OMAP3430)
/* REVISIT indexed access seemed to
* misbehave (on DaVinci) for at least peripheral IN ...
*/
#define MUSB_FLAT_REG
#endif
/* TUSB mapping: "flat" plus ep0 special cases */
#if defined(CONFIG_USB_TUSB6010)
#define musb_ep_select(_mbase, _epnum) \
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
/* "flat" mapping: each endpoint has its own i/o address */
#elif defined(MUSB_FLAT_REG)
#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum)))
#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET
/* "indexed" mapping: INDEX register controls register bank select */
#else
#define musb_ep_select(_mbase, _epnum) \
musb_writeb((_mbase), MUSB_INDEX, (_epnum))
#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET
#endif
/****************************** FUNCTIONS ********************************/
#define MUSB_HST_MODE(_musb)\
{ (_musb)->is_host = true; }
#define MUSB_DEV_MODE(_musb) \
{ (_musb)->is_host = false; }
#define test_devctl_hst_mode(_x) \
(musb_readb((_x)->mregs, MUSB_DEVCTL)&MUSB_DEVCTL_HM)
#define MUSB_MODE(musb) ((musb)->is_host ? "Host" : "Peripheral")
/******************************** TYPES *************************************/
/*
* struct musb_hw_ep - endpoint hardware (bidirectional)
*
* Ordered slightly for better cacheline locality.
*/
struct musb_hw_ep {
struct musb *musb;
void __iomem *fifo;
void __iomem *regs;
#ifdef CONFIG_USB_TUSB6010
void __iomem *conf;
#endif
/* index in musb->endpoints[] */
u8 epnum;
/* hardware configuration, possibly dynamic */
bool is_shared_fifo;
bool tx_double_buffered;
bool rx_double_buffered;
u16 max_packet_sz_tx;
u16 max_packet_sz_rx;
struct dma_channel *tx_channel;
struct dma_channel *rx_channel;
#ifdef CONFIG_USB_TUSB6010
/* TUSB has "asynchronous" and "synchronous" dma modes */
dma_addr_t fifo_async;
dma_addr_t fifo_sync;
void __iomem *fifo_sync_va;
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD
void __iomem *target_regs;
/* currently scheduled peripheral endpoint */
struct musb_qh *in_qh;
struct musb_qh *out_qh;
u8 rx_reinit;
u8 tx_reinit;
#endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* peripheral side */
struct musb_ep ep_in; /* TX */
struct musb_ep ep_out; /* RX */
#endif
};
static inline struct usb_request *next_in_request(struct musb_hw_ep *hw_ep)
{
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_in);
#else
return NULL;
#endif
}
static inline struct usb_request *next_out_request(struct musb_hw_ep *hw_ep)
{
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
return next_request(&hw_ep->ep_out);
#else
return NULL;
#endif
}
/*
* struct musb - Driver instance data.
*/
struct musb {
/* device lock */
spinlock_t lock;
struct clk *clock;
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
#define MUSB_PORT_STAT_RESUME (1 << 31)
u32 port1_status;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
unsigned long rh_timer;
enum musb_h_ep0_state ep0_stage;
/* bulk traffic normally dedicates endpoint hardware, and each
* direction has its own ring of host side endpoints.
* we try to progress the transfer at the head of each endpoint's
* queue until it completes or NAKs too much; then we try the next
* endpoint.
*/
struct musb_hw_ep *bulk_ep;
struct list_head control; /* of musb_qh */
struct list_head in_bulk; /* of musb_qh */
struct list_head out_bulk; /* of musb_qh */
struct musb_qh *periodic[32]; /* tree of interrupt+iso */
#endif
/* called with IRQs blocked; ON/nonzero implies starting a session,
* and waiting at least a_wait_vrise_tmout.
*/
void (*board_set_vbus)(struct musb *, int is_on);
struct dma_controller *dma_controller;
struct device *controller;
void __iomem *ctrl_base;
void __iomem *mregs;
#ifdef CONFIG_USB_TUSB6010
dma_addr_t async;
dma_addr_t sync;
void __iomem *sync_va;
#endif
/* passed down from chip/board specific irq handlers */
u8 int_usb;
u16 int_rx;
u16 int_tx;
struct otg_transceiver xceiv;
int nIrq;
struct musb_hw_ep endpoints[MUSB_C_NUM_EPS];
#define control_ep endpoints
#define VBUSERR_RETRY_COUNT 3
u16 vbuserr_retry;
u16 epmask;
u8 nr_endpoints;
u8 board_mode; /* enum musb_mode */
int (*board_set_power)(int state);
int (*set_clock)(struct clk *clk, int is_active);
u8 min_power; /* vbus for periph, in mA/2 */
bool is_host;
int a_wait_bcon; /* VBUS timeout in msecs */
unsigned long idle_timeout; /* Next timeout in jiffies */
/* active means connected and not suspended */
unsigned is_active:1;
unsigned is_multipoint:1;
unsigned ignore_disconnect:1; /* during bus resets */
#ifdef C_MP_TX
unsigned bulk_split:1;
#define can_bulk_split(musb,type) \
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_split)
#else
#define can_bulk_split(musb, type) 0
#endif
#ifdef C_MP_RX
unsigned bulk_combine:1;
#define can_bulk_combine(musb,type) \
(((type) == USB_ENDPOINT_XFER_BULK) && (musb)->bulk_combine)
#else
#define can_bulk_combine(musb, type) 0
#endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
/* is_self_powered is reported in device status and the
* config descriptor. is_bus_powered means B_PERIPHERAL
* draws some VBUS current; both can be true.
*/
unsigned is_self_powered:1;
unsigned is_bus_powered:1;
unsigned set_address:1;
unsigned test_mode:1;
unsigned softconnect:1;
u8 address;
u8 test_mode_nr;
u16 ackpend; /* ep0 */
enum musb_g_ep0_state ep0_state;
struct usb_gadget g; /* the gadget */
struct usb_gadget_driver *gadget_driver; /* its driver */
#endif
#ifdef MUSB_CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
#endif
};
static inline void musb_set_vbus(struct musb *musb, int is_on)
{
musb->board_set_vbus(musb, is_on);
}
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
{
return container_of(g, struct musb, g);
}
#endif
/***************************** Glue it together *****************************/
extern const char musb_driver_name[];
extern void musb_start(struct musb *musb);
extern void musb_stop(struct musb *musb);
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
extern void musb_load_testpacket(struct musb *);
extern irqreturn_t musb_interrupt(struct musb *);
extern void musb_platform_enable(struct musb *musb);
extern void musb_platform_disable(struct musb *musb);
extern void musb_hnp_stop(struct musb *musb);
extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
#if defined(CONFIG_USB_TUSB6010) || \
defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout);
#else
#define musb_platform_try_idle(x, y) do {} while (0)
#endif
#ifdef CONFIG_USB_TUSB6010
extern int musb_platform_get_vbus_status(struct musb *musb);
#else
#define musb_platform_get_vbus_status(x) 0
#endif
extern int __init musb_platform_init(struct musb *musb);
extern int musb_platform_exit(struct musb *musb);
/*-------------------------- ProcFS definitions ---------------------*/
struct proc_dir_entry;
#if (MUSB_DEBUG > 0) && defined(MUSB_CONFIG_PROC_FS)
extern struct proc_dir_entry *musb_debug_create(char *name, struct musb *data);
extern void musb_debug_delete(char *name, struct musb *data);
#else
static inline struct proc_dir_entry *
musb_debug_create(char *name, struct musb *data)
{
return NULL;
}
static inline void musb_debug_delete(char *name, struct musb *data)
{
}
#endif
#endif /* __MUSB_CORE_H__ */

View File

@ -0,0 +1,66 @@
/*
* MUSB OTG driver debug defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_LINUX_DEBUG_H__
#define __MUSB_LINUX_DEBUG_H__
#define yprintk(facility, format, args...) \
do { printk(facility "%s %d: " format , \
__func__, __LINE__ , ## args); } while (0)
#define WARNING(fmt, args...) yprintk(KERN_WARNING, fmt, ## args)
#define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args)
#define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args)
#define xprintk(level, facility, format, args...) do { \
if (_dbg_level(level)) { \
printk(facility "%s %d: " format , \
__func__, __LINE__ , ## args); \
} } while (0)
#if MUSB_DEBUG > 0
extern unsigned debug;
#else
#define debug 0
#endif
static inline int _dbg_level(unsigned l)
{
return debug >= l;
}
#define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args)
extern const char *otg_state_string(struct musb *);
#endif /* __MUSB_LINUX_DEBUG_H__ */

172
drivers/usb/musb/musb_dma.h Normal file
View File

@ -0,0 +1,172 @@
/*
* MUSB OTG driver DMA controller abstraction
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_DMA_H__
#define __MUSB_DMA_H__
struct musb_hw_ep;
/*
* DMA Controller Abstraction
*
* DMA Controllers are abstracted to allow use of a variety of different
* implementations of DMA, as allowed by the Inventra USB cores. On the
* host side, usbcore sets up the DMA mappings and flushes caches; on the
* peripheral side, the gadget controller driver does. Responsibilities
* of a DMA controller driver include:
*
* - Handling the details of moving multiple USB packets
* in cooperation with the Inventra USB core, including especially
* the correct RX side treatment of short packets and buffer-full
* states (both of which terminate transfers).
*
* - Knowing the correlation between dma channels and the
* Inventra core's local endpoint resources and data direction.
*
* - Maintaining a list of allocated/available channels.
*
* - Updating channel status on interrupts,
* whether shared with the Inventra core or separate.
*/
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#ifndef CONFIG_MUSB_PIO_ONLY
#define is_dma_capable() (1)
#else
#define is_dma_capable() (0)
#endif
#ifdef CONFIG_USB_TI_CPPI_DMA
#define is_cppi_enabled() 1
#else
#define is_cppi_enabled() 0
#endif
#ifdef CONFIG_USB_TUSB_OMAP_DMA
#define tusb_dma_omap() 1
#else
#define tusb_dma_omap() 0
#endif
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
*/
enum dma_channel_status {
/* unallocated */
MUSB_DMA_STATUS_UNKNOWN,
/* allocated ... but not busy, no errors */
MUSB_DMA_STATUS_FREE,
/* busy ... transactions are active */
MUSB_DMA_STATUS_BUSY,
/* transaction(s) aborted due to ... dma or memory bus error */
MUSB_DMA_STATUS_BUS_ABORT,
/* transaction(s) aborted due to ... core error or USB fault */
MUSB_DMA_STATUS_CORE_ABORT
};
struct dma_controller;
/**
* struct dma_channel - A DMA channel.
* @private_data: channel-private data
* @max_len: the maximum number of bytes the channel can move in one
* transaction (typically representing many USB maximum-sized packets)
* @actual_len: how many bytes have been transferred
* @status: current channel status (updated e.g. on interrupt)
* @desired_mode: true if mode 1 is desired; false if mode 0 is desired
*
* channels are associated with an endpoint for the duration of at least
* one usb transfer.
*/
struct dma_channel {
void *private_data;
/* FIXME not void* private_data, but a dma_controller * */
size_t max_len;
size_t actual_len;
enum dma_channel_status status;
bool desired_mode;
};
/*
* dma_channel_status - return status of dma channel
* @c: the channel
*
* Returns the software's view of the channel status. If that status is BUSY
* then it's possible that the hardware has completed (or aborted) a transfer,
* so the driver needs to update that status.
*/
static inline enum dma_channel_status
dma_channel_status(struct dma_channel *c)
{
return (is_dma_capable() && c) ? c->status : MUSB_DMA_STATUS_UNKNOWN;
}
/**
* struct dma_controller - A DMA Controller.
* @start: call this to start a DMA controller;
* return 0 on success, else negative errno
* @stop: call this to stop a DMA controller
* return 0 on success, else negative errno
* @channel_alloc: call this to allocate a DMA channel
* @channel_release: call this to release a DMA channel
* @channel_abort: call this to abort a pending DMA transaction,
* returning it to FREE (but allocated) state
*
* Controllers manage dma channels.
*/
struct dma_controller {
int (*start)(struct dma_controller *);
int (*stop)(struct dma_controller *);
struct dma_channel *(*channel_alloc)(struct dma_controller *,
struct musb_hw_ep *, u8 is_tx);
void (*channel_release)(struct dma_channel *);
int (*channel_program)(struct dma_channel *channel,
u16 maxpacket, u8 mode,
dma_addr_t dma_addr,
u32 length);
int (*channel_abort)(struct dma_channel *);
};
/* called after channel_program(), may indicate a fault */
extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit);
extern struct dma_controller *__init
dma_controller_create(struct musb *, void __iomem *);
extern void dma_controller_destroy(struct dma_controller *);
#endif /* __MUSB_DMA_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,108 @@
/*
* MUSB OTG driver peripheral defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_GADGET_H
#define __MUSB_GADGET_H
struct musb_request {
struct usb_request request;
struct musb_ep *ep;
struct musb *musb;
u8 tx; /* endpoint direction */
u8 epnum;
u8 mapped;
};
static inline struct musb_request *to_musb_request(struct usb_request *req)
{
return req ? container_of(req, struct musb_request, request) : NULL;
}
extern struct usb_request *
musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
extern void musb_free_request(struct usb_ep *ep, struct usb_request *req);
/*
* struct musb_ep - peripheral side view of endpoint rx or tx side
*/
struct musb_ep {
/* stuff towards the head is basically write-once. */
struct usb_ep end_point;
char name[12];
struct musb_hw_ep *hw_ep;
struct musb *musb;
u8 current_epnum;
/* ... when enabled/disabled ... */
u8 type;
u8 is_in;
u16 packet_sz;
const struct usb_endpoint_descriptor *desc;
struct dma_channel *dma;
/* later things are modified based on usage */
struct list_head req_list;
/* true if lock must be dropped but req_list may not be advanced */
u8 busy;
};
static inline struct musb_ep *to_musb_ep(struct usb_ep *ep)
{
return ep ? container_of(ep, struct musb_ep, end_point) : NULL;
}
static inline struct usb_request *next_request(struct musb_ep *ep)
{
struct list_head *queue = &ep->req_list;
if (list_empty(queue))
return NULL;
return container_of(queue->next, struct usb_request, list);
}
extern void musb_g_tx(struct musb *musb, u8 epnum);
extern void musb_g_rx(struct musb *musb, u8 epnum);
extern const struct usb_ep_ops musb_g_ep0_ops;
extern int musb_gadget_setup(struct musb *);
extern void musb_gadget_cleanup(struct musb *);
extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int);
extern int musb_gadget_set_halt(struct usb_ep *ep, int value);
#endif /* __MUSB_GADGET_H */

View File

@ -0,0 +1,981 @@
/*
* MUSB OTG peripheral driver ep0 handling
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include "musb_core.h"
/* ep0 is always musb->endpoints[0].ep_in */
#define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0])
/*
* locking note: we use only the controller lock, for simpler correctness.
* It's always held with IRQs blocked.
*
* It protects the ep0 request queue as well as ep0_state, not just the
* controller and indexed registers. And that lock stays held unless it
* needs to be dropped to allow reentering this driver ... like upcalls to
* the gadget driver, or adjusting endpoint halt status.
*/
static char *decode_ep0stage(u8 stage)
{
switch (stage) {
case MUSB_EP0_STAGE_SETUP: return "idle";
case MUSB_EP0_STAGE_TX: return "in";
case MUSB_EP0_STAGE_RX: return "out";
case MUSB_EP0_STAGE_ACKWAIT: return "wait";
case MUSB_EP0_STAGE_STATUSIN: return "in/status";
case MUSB_EP0_STAGE_STATUSOUT: return "out/status";
default: return "?";
}
}
/* handle a standard GET_STATUS request
* Context: caller holds controller lock
*/
static int service_tx_status_request(
struct musb *musb,
const struct usb_ctrlrequest *ctrlrequest)
{
void __iomem *mbase = musb->mregs;
int handled = 1;
u8 result[2], epnum = 0;
const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK;
result[1] = 0;
switch (recip) {
case USB_RECIP_DEVICE:
result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
#ifdef CONFIG_USB_MUSB_OTG
if (musb->g.is_otg) {
result[0] |= musb->g.b_hnp_enable
<< USB_DEVICE_B_HNP_ENABLE;
result[0] |= musb->g.a_alt_hnp_support
<< USB_DEVICE_A_ALT_HNP_SUPPORT;
result[0] |= musb->g.a_hnp_support
<< USB_DEVICE_A_HNP_SUPPORT;
}
#endif
break;
case USB_RECIP_INTERFACE:
result[0] = 0;
break;
case USB_RECIP_ENDPOINT: {
int is_in;
struct musb_ep *ep;
u16 tmp;
void __iomem *regs;
epnum = (u8) ctrlrequest->wIndex;
if (!epnum) {
result[0] = 0;
break;
}
is_in = epnum & USB_DIR_IN;
if (is_in) {
epnum &= 0x0f;
ep = &musb->endpoints[epnum].ep_in;
} else {
ep = &musb->endpoints[epnum].ep_out;
}
regs = musb->endpoints[epnum].regs;
if (epnum >= MUSB_C_NUM_EPS || !ep->desc) {
handled = -EINVAL;
break;
}
musb_ep_select(mbase, epnum);
if (is_in)
tmp = musb_readw(regs, MUSB_TXCSR)
& MUSB_TXCSR_P_SENDSTALL;
else
tmp = musb_readw(regs, MUSB_RXCSR)
& MUSB_RXCSR_P_SENDSTALL;
musb_ep_select(mbase, 0);
result[0] = tmp ? 1 : 0;
} break;
default:
/* class, vendor, etc ... delegate */
handled = 0;
break;
}
/* fill up the fifo; caller updates csr0 */
if (handled > 0) {
u16 len = le16_to_cpu(ctrlrequest->wLength);
if (len > 2)
len = 2;
musb_write_fifo(&musb->endpoints[0], len, result);
}
return handled;
}
/*
* handle a control-IN request, the end0 buffer contains the current request
* that is supposed to be a standard control request. Assumes the fifo to
* be at least 2 bytes long.
*
* @return 0 if the request was NOT HANDLED,
* < 0 when error
* > 0 when the request is processed
*
* Context: caller holds controller lock
*/
static int
service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest)
{
int handled = 0; /* not handled */
if ((ctrlrequest->bRequestType & USB_TYPE_MASK)
== USB_TYPE_STANDARD) {
switch (ctrlrequest->bRequest) {
case USB_REQ_GET_STATUS:
handled = service_tx_status_request(musb,
ctrlrequest);
break;
/* case USB_REQ_SYNC_FRAME: */
default:
break;
}
}
return handled;
}
/*
* Context: caller holds controller lock
*/
static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req)
{
musb_g_giveback(&musb->endpoints[0].ep_in, req, 0);
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
}
/*
* Tries to start B-device HNP negotiation if enabled via sysfs
*/
static inline void musb_try_b_hnp_enable(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
u8 devctl;
DBG(1, "HNP: Setting HR\n");
devctl = musb_readb(mbase, MUSB_DEVCTL);
musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR);
}
/*
* Handle all control requests with no DATA stage, including standard
* requests such as:
* USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized
* always delegated to the gadget driver
* USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE
* always handled here, except for class/vendor/... features
*
* Context: caller holds controller lock
*/
static int
service_zero_data_request(struct musb *musb,
struct usb_ctrlrequest *ctrlrequest)
__releases(musb->lock)
__acquires(musb->lock)
{
int handled = -EINVAL;
void __iomem *mbase = musb->mregs;
const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK;
/* the gadget driver handles everything except what we MUST handle */
if ((ctrlrequest->bRequestType & USB_TYPE_MASK)
== USB_TYPE_STANDARD) {
switch (ctrlrequest->bRequest) {
case USB_REQ_SET_ADDRESS:
/* change it after the status stage */
musb->set_address = true;
musb->address = (u8) (ctrlrequest->wValue & 0x7f);
handled = 1;
break;
case USB_REQ_CLEAR_FEATURE:
switch (recip) {
case USB_RECIP_DEVICE:
if (ctrlrequest->wValue
!= USB_DEVICE_REMOTE_WAKEUP)
break;
musb->may_wakeup = 0;
handled = 1;
break;
case USB_RECIP_INTERFACE:
break;
case USB_RECIP_ENDPOINT:{
const u8 num = ctrlrequest->wIndex & 0x0f;
struct musb_ep *musb_ep;
if (num == 0
|| num >= MUSB_C_NUM_EPS
|| ctrlrequest->wValue
!= USB_ENDPOINT_HALT)
break;
if (ctrlrequest->wIndex & USB_DIR_IN)
musb_ep = &musb->endpoints[num].ep_in;
else
musb_ep = &musb->endpoints[num].ep_out;
if (!musb_ep->desc)
break;
/* REVISIT do it directly, no locking games */
spin_unlock(&musb->lock);
musb_gadget_set_halt(&musb_ep->end_point, 0);
spin_lock(&musb->lock);
/* select ep0 again */
musb_ep_select(mbase, 0);
handled = 1;
} break;
default:
/* class, vendor, etc ... delegate */
handled = 0;
break;
}
break;
case USB_REQ_SET_FEATURE:
switch (recip) {
case USB_RECIP_DEVICE:
handled = 1;
switch (ctrlrequest->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
musb->may_wakeup = 1;
break;
case USB_DEVICE_TEST_MODE:
if (musb->g.speed != USB_SPEED_HIGH)
goto stall;
if (ctrlrequest->wIndex & 0xff)
goto stall;
switch (ctrlrequest->wIndex >> 8) {
case 1:
pr_debug("TEST_J\n");
/* TEST_J */
musb->test_mode_nr =
MUSB_TEST_J;
break;
case 2:
/* TEST_K */
pr_debug("TEST_K\n");
musb->test_mode_nr =
MUSB_TEST_K;
break;
case 3:
/* TEST_SE0_NAK */
pr_debug("TEST_SE0_NAK\n");
musb->test_mode_nr =
MUSB_TEST_SE0_NAK;
break;
case 4:
/* TEST_PACKET */
pr_debug("TEST_PACKET\n");
musb->test_mode_nr =
MUSB_TEST_PACKET;
break;
default:
goto stall;
}
/* enter test mode after irq */
if (handled > 0)
musb->test_mode = true;
break;
#ifdef CONFIG_USB_MUSB_OTG
case USB_DEVICE_B_HNP_ENABLE:
if (!musb->g.is_otg)
goto stall;
musb->g.b_hnp_enable = 1;
musb_try_b_hnp_enable(musb);
break;
case USB_DEVICE_A_HNP_SUPPORT:
if (!musb->g.is_otg)
goto stall;
musb->g.a_hnp_support = 1;
break;
case USB_DEVICE_A_ALT_HNP_SUPPORT:
if (!musb->g.is_otg)
goto stall;
musb->g.a_alt_hnp_support = 1;
break;
#endif
stall:
default:
handled = -EINVAL;
break;
}
break;
case USB_RECIP_INTERFACE:
break;
case USB_RECIP_ENDPOINT:{
const u8 epnum =
ctrlrequest->wIndex & 0x0f;
struct musb_ep *musb_ep;
struct musb_hw_ep *ep;
void __iomem *regs;
int is_in;
u16 csr;
if (epnum == 0
|| epnum >= MUSB_C_NUM_EPS
|| ctrlrequest->wValue
!= USB_ENDPOINT_HALT)
break;
ep = musb->endpoints + epnum;
regs = ep->regs;
is_in = ctrlrequest->wIndex & USB_DIR_IN;
if (is_in)
musb_ep = &ep->ep_in;
else
musb_ep = &ep->ep_out;
if (!musb_ep->desc)
break;
musb_ep_select(mbase, epnum);
if (is_in) {
csr = musb_readw(regs,
MUSB_TXCSR);
if (csr & MUSB_TXCSR_FIFONOTEMPTY)
csr |= MUSB_TXCSR_FLUSHFIFO;
csr |= MUSB_TXCSR_P_SENDSTALL
| MUSB_TXCSR_CLRDATATOG
| MUSB_TXCSR_P_WZC_BITS;
musb_writew(regs, MUSB_TXCSR,
csr);
} else {
csr = musb_readw(regs,
MUSB_RXCSR);
csr |= MUSB_RXCSR_P_SENDSTALL
| MUSB_RXCSR_FLUSHFIFO
| MUSB_RXCSR_CLRDATATOG
| MUSB_TXCSR_P_WZC_BITS;
musb_writew(regs, MUSB_RXCSR,
csr);
}
/* select ep0 again */
musb_ep_select(mbase, 0);
handled = 1;
} break;
default:
/* class, vendor, etc ... delegate */
handled = 0;
break;
}
break;
default:
/* delegate SET_CONFIGURATION, etc */
handled = 0;
}
} else
handled = 0;
return handled;
}
/* we have an ep0out data packet
* Context: caller holds controller lock
*/
static void ep0_rxstate(struct musb *musb)
{
void __iomem *regs = musb->control_ep->regs;
struct usb_request *req;
u16 tmp;
req = next_ep0_request(musb);
/* read packet and ack; or stall because of gadget driver bug:
* should have provided the rx buffer before setup() returned.
*/
if (req) {
void *buf = req->buf + req->actual;
unsigned len = req->length - req->actual;
/* read the buffer */
tmp = musb_readb(regs, MUSB_COUNT0);
if (tmp > len) {
req->status = -EOVERFLOW;
tmp = len;
}
musb_read_fifo(&musb->endpoints[0], tmp, buf);
req->actual += tmp;
tmp = MUSB_CSR0_P_SVDRXPKTRDY;
if (tmp < 64 || req->actual == req->length) {
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
tmp |= MUSB_CSR0_P_DATAEND;
} else
req = NULL;
} else
tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;
/* Completion handler may choose to stall, e.g. because the
* message just received holds invalid data.
*/
if (req) {
musb->ackpend = tmp;
musb_g_ep0_giveback(musb, req);
if (!musb->ackpend)
return;
musb->ackpend = 0;
}
musb_writew(regs, MUSB_CSR0, tmp);
}
/*
* transmitting to the host (IN), this code might be called from IRQ
* and from kernel thread.
*
* Context: caller holds controller lock
*/
static void ep0_txstate(struct musb *musb)
{
void __iomem *regs = musb->control_ep->regs;
struct usb_request *request = next_ep0_request(musb);
u16 csr = MUSB_CSR0_TXPKTRDY;
u8 *fifo_src;
u8 fifo_count;
if (!request) {
/* WARN_ON(1); */
DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0));
return;
}
/* load the data */
fifo_src = (u8 *) request->buf + request->actual;
fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE,
request->length - request->actual);
musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src);
request->actual += fifo_count;
/* update the flags */
if (fifo_count < MUSB_MAX_END0_PACKET
|| request->actual == request->length) {
musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT;
csr |= MUSB_CSR0_P_DATAEND;
} else
request = NULL;
/* report completions as soon as the fifo's loaded; there's no
* win in waiting till this last packet gets acked. (other than
* very precise fault reporting, needed by USB TMC; possible with
* this hardware, but not usable from portable gadget drivers.)
*/
if (request) {
musb->ackpend = csr;
musb_g_ep0_giveback(musb, request);
if (!musb->ackpend)
return;
musb->ackpend = 0;
}
/* send it out, triggering a "txpktrdy cleared" irq */
musb_writew(regs, MUSB_CSR0, csr);
}
/*
* Read a SETUP packet (struct usb_ctrlrequest) from the hardware.
* Fields are left in USB byte-order.
*
* Context: caller holds controller lock.
*/
static void
musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req)
{
struct usb_request *r;
void __iomem *regs = musb->control_ep->regs;
musb_read_fifo(&musb->endpoints[0], sizeof *req, (u8 *)req);
/* NOTE: earlier 2.6 versions changed setup packets to host
* order, but now USB packets always stay in USB byte order.
*/
DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n",
req->bRequestType,
req->bRequest,
le16_to_cpu(req->wValue),
le16_to_cpu(req->wIndex),
le16_to_cpu(req->wLength));
/* clean up any leftover transfers */
r = next_ep0_request(musb);
if (r)
musb_g_ep0_giveback(musb, r);
/* For zero-data requests we want to delay the STATUS stage to
* avoid SETUPEND errors. If we read data (OUT), delay accepting
* packets until there's a buffer to store them in.
*
* If we write data, the controller acts happier if we enable
* the TX FIFO right away, and give the controller a moment
* to switch modes...
*/
musb->set_address = false;
musb->ackpend = MUSB_CSR0_P_SVDRXPKTRDY;
if (req->wLength == 0) {
if (req->bRequestType & USB_DIR_IN)
musb->ackpend |= MUSB_CSR0_TXPKTRDY;
musb->ep0_state = MUSB_EP0_STAGE_ACKWAIT;
} else if (req->bRequestType & USB_DIR_IN) {
musb->ep0_state = MUSB_EP0_STAGE_TX;
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDRXPKTRDY);
while ((musb_readw(regs, MUSB_CSR0)
& MUSB_CSR0_RXPKTRDY) != 0)
cpu_relax();
musb->ackpend = 0;
} else
musb->ep0_state = MUSB_EP0_STAGE_RX;
}
static int
forward_to_driver(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest)
__releases(musb->lock)
__acquires(musb->lock)
{
int retval;
if (!musb->gadget_driver)
return -EOPNOTSUPP;
spin_unlock(&musb->lock);
retval = musb->gadget_driver->setup(&musb->g, ctrlrequest);
spin_lock(&musb->lock);
return retval;
}
/*
* Handle peripheral ep0 interrupt
*
* Context: irq handler; we won't re-enter the driver that way.
*/
irqreturn_t musb_g_ep0_irq(struct musb *musb)
{
u16 csr;
u16 len;
void __iomem *mbase = musb->mregs;
void __iomem *regs = musb->endpoints[0].regs;
irqreturn_t retval = IRQ_NONE;
musb_ep_select(mbase, 0); /* select ep0 */
csr = musb_readw(regs, MUSB_CSR0);
len = musb_readb(regs, MUSB_COUNT0);
DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n",
csr, len,
musb_readb(mbase, MUSB_FADDR),
decode_ep0stage(musb->ep0_state));
/* I sent a stall.. need to acknowledge it now.. */
if (csr & MUSB_CSR0_P_SENTSTALL) {
musb_writew(regs, MUSB_CSR0,
csr & ~MUSB_CSR0_P_SENTSTALL);
retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
csr = musb_readw(regs, MUSB_CSR0);
}
/* request ended "early" */
if (csr & MUSB_CSR0_P_SETUPEND) {
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SVDSETUPEND);
retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
csr = musb_readw(regs, MUSB_CSR0);
/* NOTE: request may need completion */
}
/* docs from Mentor only describe tx, rx, and idle/setup states.
* we need to handle nuances around status stages, and also the
* case where status and setup stages come back-to-back ...
*/
switch (musb->ep0_state) {
case MUSB_EP0_STAGE_TX:
/* irq on clearing txpktrdy */
if ((csr & MUSB_CSR0_TXPKTRDY) == 0) {
ep0_txstate(musb);
retval = IRQ_HANDLED;
}
break;
case MUSB_EP0_STAGE_RX:
/* irq on set rxpktrdy */
if (csr & MUSB_CSR0_RXPKTRDY) {
ep0_rxstate(musb);
retval = IRQ_HANDLED;
}
break;
case MUSB_EP0_STAGE_STATUSIN:
/* end of sequence #2 (OUT/RX state) or #3 (no data) */
/* update address (if needed) only @ the end of the
* status phase per usb spec, which also guarantees
* we get 10 msec to receive this irq... until this
* is done we won't see the next packet.
*/
if (musb->set_address) {
musb->set_address = false;
musb_writeb(mbase, MUSB_FADDR, musb->address);
}
/* enter test mode if needed (exit by reset) */
else if (musb->test_mode) {
DBG(1, "entering TESTMODE\n");
if (MUSB_TEST_PACKET == musb->test_mode_nr)
musb_load_testpacket(musb);
musb_writeb(mbase, MUSB_TESTMODE,
musb->test_mode_nr);
}
/* FALLTHROUGH */
case MUSB_EP0_STAGE_STATUSOUT:
/* end of sequence #1: write to host (TX state) */
{
struct usb_request *req;
req = next_ep0_request(musb);
if (req)
musb_g_ep0_giveback(musb, req);
}
retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
/* FALLTHROUGH */
case MUSB_EP0_STAGE_SETUP:
if (csr & MUSB_CSR0_RXPKTRDY) {
struct usb_ctrlrequest setup;
int handled = 0;
if (len != 8) {
ERR("SETUP packet len %d != 8 ?\n", len);
break;
}
musb_read_setup(musb, &setup);
retval = IRQ_HANDLED;
/* sometimes the RESET won't be reported */
if (unlikely(musb->g.speed == USB_SPEED_UNKNOWN)) {
u8 power;
printk(KERN_NOTICE "%s: peripheral reset "
"irq lost!\n",
musb_driver_name);
power = musb_readb(mbase, MUSB_POWER);
musb->g.speed = (power & MUSB_POWER_HSMODE)
? USB_SPEED_HIGH : USB_SPEED_FULL;
}
switch (musb->ep0_state) {
/* sequence #3 (no data stage), includes requests
* we can't forward (notably SET_ADDRESS and the
* device/endpoint feature set/clear operations)
* plus SET_CONFIGURATION and others we must
*/
case MUSB_EP0_STAGE_ACKWAIT:
handled = service_zero_data_request(
musb, &setup);
/* status stage might be immediate */
if (handled > 0) {
musb->ackpend |= MUSB_CSR0_P_DATAEND;
musb->ep0_state =
MUSB_EP0_STAGE_STATUSIN;
}
break;
/* sequence #1 (IN to host), includes GET_STATUS
* requests that we can't forward, GET_DESCRIPTOR
* and others that we must
*/
case MUSB_EP0_STAGE_TX:
handled = service_in_request(musb, &setup);
if (handled > 0) {
musb->ackpend = MUSB_CSR0_TXPKTRDY
| MUSB_CSR0_P_DATAEND;
musb->ep0_state =
MUSB_EP0_STAGE_STATUSOUT;
}
break;
/* sequence #2 (OUT from host), always forward */
default: /* MUSB_EP0_STAGE_RX */
break;
}
DBG(3, "handled %d, csr %04x, ep0stage %s\n",
handled, csr,
decode_ep0stage(musb->ep0_state));
/* unless we need to delegate this to the gadget
* driver, we know how to wrap this up: csr0 has
* not yet been written.
*/
if (handled < 0)
goto stall;
else if (handled > 0)
goto finish;
handled = forward_to_driver(musb, &setup);
if (handled < 0) {
musb_ep_select(mbase, 0);
stall:
DBG(3, "stall (%d)\n", handled);
musb->ackpend |= MUSB_CSR0_P_SENDSTALL;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
finish:
musb_writew(regs, MUSB_CSR0,
musb->ackpend);
musb->ackpend = 0;
}
}
break;
case MUSB_EP0_STAGE_ACKWAIT:
/* This should not happen. But happens with tusb6010 with
* g_file_storage and high speed. Do nothing.
*/
retval = IRQ_HANDLED;
break;
default:
/* "can't happen" */
WARN_ON(1);
musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
break;
}
return retval;
}
static int
musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
{
/* always enabled */
return -EINVAL;
}
static int musb_g_ep0_disable(struct usb_ep *e)
{
/* always enabled */
return -EINVAL;
}
static int
musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
{
struct musb_ep *ep;
struct musb_request *req;
struct musb *musb;
int status;
unsigned long lockflags;
void __iomem *regs;
if (!e || !r)
return -EINVAL;
ep = to_musb_ep(e);
musb = ep->musb;
regs = musb->control_ep->regs;
req = to_musb_request(r);
req->musb = musb;
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->tx = ep->is_in;
spin_lock_irqsave(&musb->lock, lockflags);
if (!list_empty(&ep->req_list)) {
status = -EBUSY;
goto cleanup;
}
switch (musb->ep0_state) {
case MUSB_EP0_STAGE_RX: /* control-OUT data */
case MUSB_EP0_STAGE_TX: /* control-IN data */
case MUSB_EP0_STAGE_ACKWAIT: /* zero-length data */
status = 0;
break;
default:
DBG(1, "ep0 request queued in state %d\n",
musb->ep0_state);
status = -EINVAL;
goto cleanup;
}
/* add request to the list */
list_add_tail(&(req->request.list), &(ep->req_list));
DBG(3, "queue to %s (%s), length=%d\n",
ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
req->request.length);
musb_ep_select(musb->mregs, 0);
/* sequence #1, IN ... start writing the data */
if (musb->ep0_state == MUSB_EP0_STAGE_TX)
ep0_txstate(musb);
/* sequence #3, no-data ... issue IN status */
else if (musb->ep0_state == MUSB_EP0_STAGE_ACKWAIT) {
if (req->request.length)
status = -EINVAL;
else {
musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
musb_writew(regs, MUSB_CSR0,
musb->ackpend | MUSB_CSR0_P_DATAEND);
musb->ackpend = 0;
musb_g_ep0_giveback(ep->musb, r);
}
/* else for sequence #2 (OUT), caller provides a buffer
* before the next packet arrives. deferred responses
* (after SETUP is acked) are racey.
*/
} else if (musb->ackpend) {
musb_writew(regs, MUSB_CSR0, musb->ackpend);
musb->ackpend = 0;
}
cleanup:
spin_unlock_irqrestore(&musb->lock, lockflags);
return status;
}
static int musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
{
/* we just won't support this */
return -EINVAL;
}
static int musb_g_ep0_halt(struct usb_ep *e, int value)
{
struct musb_ep *ep;
struct musb *musb;
void __iomem *base, *regs;
unsigned long flags;
int status;
u16 csr;
if (!e || !value)
return -EINVAL;
ep = to_musb_ep(e);
musb = ep->musb;
base = musb->mregs;
regs = musb->control_ep->regs;
status = 0;
spin_lock_irqsave(&musb->lock, flags);
if (!list_empty(&ep->req_list)) {
status = -EBUSY;
goto cleanup;
}
musb_ep_select(base, 0);
csr = musb->ackpend;
switch (musb->ep0_state) {
/* Stalls are usually issued after parsing SETUP packet, either
* directly in irq context from setup() or else later.
*/
case MUSB_EP0_STAGE_TX: /* control-IN data */
case MUSB_EP0_STAGE_ACKWAIT: /* STALL for zero-length data */
case MUSB_EP0_STAGE_RX: /* control-OUT data */
csr = musb_readw(regs, MUSB_CSR0);
/* FALLTHROUGH */
/* It's also OK to issue stalls during callbacks when a non-empty
* DATA stage buffer has been read (or even written).
*/
case MUSB_EP0_STAGE_STATUSIN: /* control-OUT status */
case MUSB_EP0_STAGE_STATUSOUT: /* control-IN status */
csr |= MUSB_CSR0_P_SENDSTALL;
musb_writew(regs, MUSB_CSR0, csr);
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
musb->ackpend = 0;
break;
default:
DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state);
status = -EINVAL;
}
cleanup:
spin_unlock_irqrestore(&musb->lock, flags);
return status;
}
const struct usb_ep_ops musb_g_ep0_ops = {
.enable = musb_g_ep0_enable,
.disable = musb_g_ep0_disable,
.alloc_request = musb_alloc_request,
.free_request = musb_free_request,
.queue = musb_g_ep0_queue,
.dequeue = musb_g_ep0_dequeue,
.set_halt = musb_g_ep0_halt,
};

2170
drivers/usb/musb/musb_host.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
/*
* MUSB OTG driver host defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _MUSB_HOST_H
#define _MUSB_HOST_H
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)
{
return container_of((void *) musb, struct usb_hcd, hcd_priv);
}
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
{
return (struct musb *) (hcd->hcd_priv);
}
/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */
struct musb_qh {
struct usb_host_endpoint *hep; /* usbcore info */
struct usb_device *dev;
struct musb_hw_ep *hw_ep; /* current binding */
struct list_head ring; /* of musb_qh */
/* struct musb_qh *next; */ /* for periodic tree */
unsigned offset; /* in urb->transfer_buffer */
unsigned segsize; /* current xfer fragment */
u8 type_reg; /* {rx,tx} type register */
u8 intv_reg; /* {rx,tx} interval register */
u8 addr_reg; /* device address register */
u8 h_addr_reg; /* hub address register */
u8 h_port_reg; /* hub port register */
u8 is_ready; /* safe to modify hw_ep */
u8 type; /* XFERTYPE_* */
u8 epnum;
u16 maxpacket;
u16 frame; /* for periodic schedule */
unsigned iso_idx; /* in urb->iso_frame_desc[] */
};
/* map from control or bulk queue head to the first qh on that ring */
static inline struct musb_qh *first_qh(struct list_head *q)
{
if (list_empty(q))
return NULL;
return list_entry(q->next, struct musb_qh, ring);
}
extern void musb_root_disconnect(struct musb *musb);
struct usb_hcd;
extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf);
extern int musb_hub_control(struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
extern const struct hc_driver musb_hc_driver;
static inline struct urb *next_urb(struct musb_qh *qh)
{
#ifdef CONFIG_USB_MUSB_HDRC_HCD
struct list_head *queue;
if (!qh)
return NULL;
queue = &qh->hep->urb_list;
if (list_empty(queue))
return NULL;
return list_entry(queue->next, struct urb, urb_list);
#else
return NULL;
#endif
}
#endif /* _MUSB_HOST_H */

115
drivers/usb/musb/musb_io.h Normal file
View File

@ -0,0 +1,115 @@
/*
* MUSB OTG driver register I/O
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_LINUX_PLATFORM_ARCH_H__
#define __MUSB_LINUX_PLATFORM_ARCH_H__
#include <linux/io.h>
#ifndef CONFIG_ARM
static inline void readsl(const void __iomem *addr, void *buf, int len)
{ insl((unsigned long)addr, buf, len); }
static inline void readsw(const void __iomem *addr, void *buf, int len)
{ insw((unsigned long)addr, buf, len); }
static inline void readsb(const void __iomem *addr, void *buf, int len)
{ insb((unsigned long)addr, buf, len); }
static inline void writesl(const void __iomem *addr, const void *buf, int len)
{ outsl((unsigned long)addr, buf, len); }
static inline void writesw(const void __iomem *addr, const void *buf, int len)
{ outsw((unsigned long)addr, buf, len); }
static inline void writesb(const void __iomem *addr, const void *buf, int len)
{ outsb((unsigned long)addr, buf, len); }
#endif
/* NOTE: these offsets are all in bytes */
static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
{ return __raw_readw(addr + offset); }
static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
{ return __raw_readl(addr + offset); }
static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
{ __raw_writew(data, addr + offset); }
static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
{ __raw_writel(data, addr + offset); }
#ifdef CONFIG_USB_TUSB6010
/*
* TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
*/
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
{
u16 tmp;
u8 val;
tmp = __raw_readw(addr + (offset & ~1));
if (offset & 1)
val = (tmp >> 8);
else
val = tmp & 0xff;
return val;
}
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{
u16 tmp;
tmp = __raw_readw(addr + (offset & ~1));
if (offset & 1)
tmp = (data << 8) | (tmp & 0xff);
else
tmp = (tmp & 0xff00) | data;
__raw_writew(tmp, addr + (offset & ~1));
}
#else
static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
{ return __raw_readb(addr + offset); }
static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
{ __raw_writeb(data, addr + offset); }
#endif /* CONFIG_USB_TUSB6010 */
#endif

View File

@ -0,0 +1,830 @@
/*
* MUSB OTG driver debug support
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h> /* FIXME remove procfs writes */
#include <asm/arch/hardware.h>
#include "musb_core.h"
#include "davinci.h"
#ifdef CONFIG_USB_MUSB_HDRC_HCD
static int dump_qh(struct musb_qh *qh, char *buf, unsigned max)
{
int count;
int tmp;
struct usb_host_endpoint *hep = qh->hep;
struct urb *urb;
count = snprintf(buf, max, " qh %p dev%d ep%d%s max%d\n",
qh, qh->dev->devnum, qh->epnum,
({ char *s; switch (qh->type) {
case USB_ENDPOINT_XFER_BULK:
s = "-bulk"; break;
case USB_ENDPOINT_XFER_INT:
s = "-int"; break;
case USB_ENDPOINT_XFER_CONTROL:
s = ""; break;
default:
s = "iso"; break;
}; s; }),
qh->maxpacket);
if (count <= 0)
return 0;
buf += count;
max -= count;
list_for_each_entry(urb, &hep->urb_list, urb_list) {
tmp = snprintf(buf, max, "\t%s urb %p %d/%d\n",
usb_pipein(urb->pipe) ? "in" : "out",
urb, urb->actual_length,
urb->transfer_buffer_length);
if (tmp <= 0)
break;
tmp = min(tmp, (int)max);
count += tmp;
buf += tmp;
max -= tmp;
}
return count;
}
static int
dump_queue(struct list_head *q, char *buf, unsigned max)
{
int count = 0;
struct musb_qh *qh;
list_for_each_entry(qh, q, ring) {
int tmp;
tmp = dump_qh(qh, buf, max);
if (tmp <= 0)
break;
tmp = min(tmp, (int)max);
count += tmp;
buf += tmp;
max -= tmp;
}
return count;
}
#endif /* HCD */
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
static int dump_ep(struct musb_ep *ep, char *buffer, unsigned max)
{
char *buf = buffer;
int code = 0;
void __iomem *regs = ep->hw_ep->regs;
char *mode = "1buf";
if (ep->is_in) {
if (ep->hw_ep->tx_double_buffered)
mode = "2buf";
} else {
if (ep->hw_ep->rx_double_buffered)
mode = "2buf";
}
do {
struct usb_request *req;
code = snprintf(buf, max,
"\n%s (hw%d): %s%s, csr %04x maxp %04x\n",
ep->name, ep->current_epnum,
mode, ep->dma ? " dma" : "",
musb_readw(regs,
(ep->is_in || !ep->current_epnum)
? MUSB_TXCSR
: MUSB_RXCSR),
musb_readw(regs, ep->is_in
? MUSB_TXMAXP
: MUSB_RXMAXP)
);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
if (is_cppi_enabled() && ep->current_epnum) {
unsigned cppi = ep->current_epnum - 1;
void __iomem *base = ep->musb->ctrl_base;
unsigned off1 = cppi << 2;
void __iomem *ram = base;
char tmp[16];
if (ep->is_in) {
ram += DAVINCI_TXCPPI_STATERAM_OFFSET(cppi);
tmp[0] = 0;
} else {
ram += DAVINCI_RXCPPI_STATERAM_OFFSET(cppi);
snprintf(tmp, sizeof tmp, "%d left, ",
musb_readl(base,
DAVINCI_RXCPPI_BUFCNT0_REG + off1));
}
code = snprintf(buf, max, "%cX DMA%d: %s"
"%08x %08x, %08x %08x; "
"%08x %08x %08x .. %08x\n",
ep->is_in ? 'T' : 'R',
ep->current_epnum - 1, tmp,
musb_readl(ram, 0 * 4),
musb_readl(ram, 1 * 4),
musb_readl(ram, 2 * 4),
musb_readl(ram, 3 * 4),
musb_readl(ram, 4 * 4),
musb_readl(ram, 5 * 4),
musb_readl(ram, 6 * 4),
musb_readl(ram, 7 * 4));
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
if (list_empty(&ep->req_list)) {
code = snprintf(buf, max, "\t(queue empty)\n");
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
break;
}
list_for_each_entry(req, &ep->req_list, list) {
code = snprintf(buf, max, "\treq %p, %s%s%d/%d\n",
req,
req->zero ? "zero, " : "",
req->short_not_ok ? "!short, " : "",
req->actual, req->length);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
} while (0);
return buf - buffer;
}
#endif
static int
dump_end_info(struct musb *musb, u8 epnum, char *aBuffer, unsigned max)
{
int code = 0;
char *buf = aBuffer;
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
do {
musb_ep_select(musb->mregs, epnum);
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (is_host_active(musb)) {
int dump_rx, dump_tx;
void __iomem *regs = hw_ep->regs;
/* TEMPORARY (!) until we have a real periodic
* schedule tree ...
*/
if (!epnum) {
/* control is shared, uses RX queue
* but (mostly) shadowed tx registers
*/
dump_tx = !list_empty(&musb->control);
dump_rx = 0;
} else if (hw_ep == musb->bulk_ep) {
dump_tx = !list_empty(&musb->out_bulk);
dump_rx = !list_empty(&musb->in_bulk);
} else if (musb->periodic[epnum]) {
struct usb_host_endpoint *hep;
hep = musb->periodic[epnum]->hep;
dump_rx = hep->desc.bEndpointAddress
& USB_ENDPOINT_DIR_MASK;
dump_tx = !dump_rx;
} else
break;
/* END TEMPORARY */
if (dump_rx) {
code = snprintf(buf, max,
"\nRX%d: %s rxcsr %04x interval %02x "
"max %04x type %02x; "
"dev %d hub %d port %d"
"\n",
epnum,
hw_ep->rx_double_buffered
? "2buf" : "1buf",
musb_readw(regs, MUSB_RXCSR),
musb_readb(regs, MUSB_RXINTERVAL),
musb_readw(regs, MUSB_RXMAXP),
musb_readb(regs, MUSB_RXTYPE),
/* FIXME: assumes multipoint */
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_RXFUNCADDR)),
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_RXHUBADDR)),
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_RXHUBPORT))
);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
if (is_cppi_enabled()
&& epnum
&& hw_ep->rx_channel) {
unsigned cppi = epnum - 1;
unsigned off1 = cppi << 2;
void __iomem *base;
void __iomem *ram;
char tmp[16];
base = musb->ctrl_base;
ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
cppi) + base;
snprintf(tmp, sizeof tmp, "%d left, ",
musb_readl(base,
DAVINCI_RXCPPI_BUFCNT0_REG
+ off1));
code = snprintf(buf, max,
" rx dma%d: %s"
"%08x %08x, %08x %08x; "
"%08x %08x %08x .. %08x\n",
cppi, tmp,
musb_readl(ram, 0 * 4),
musb_readl(ram, 1 * 4),
musb_readl(ram, 2 * 4),
musb_readl(ram, 3 * 4),
musb_readl(ram, 4 * 4),
musb_readl(ram, 5 * 4),
musb_readl(ram, 6 * 4),
musb_readl(ram, 7 * 4));
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
if (hw_ep == musb->bulk_ep
&& !list_empty(
&musb->in_bulk)) {
code = dump_queue(&musb->in_bulk,
buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
} else if (musb->periodic[epnum]) {
code = dump_qh(musb->periodic[epnum],
buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
}
if (dump_tx) {
code = snprintf(buf, max,
"\nTX%d: %s txcsr %04x interval %02x "
"max %04x type %02x; "
"dev %d hub %d port %d"
"\n",
epnum,
hw_ep->tx_double_buffered
? "2buf" : "1buf",
musb_readw(regs, MUSB_TXCSR),
musb_readb(regs, MUSB_TXINTERVAL),
musb_readw(regs, MUSB_TXMAXP),
musb_readb(regs, MUSB_TXTYPE),
/* FIXME: assumes multipoint */
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_TXFUNCADDR)),
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_TXHUBADDR)),
musb_readb(musb->mregs,
MUSB_BUSCTL_OFFSET(epnum,
MUSB_TXHUBPORT))
);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
if (is_cppi_enabled()
&& epnum
&& hw_ep->tx_channel) {
unsigned cppi = epnum - 1;
void __iomem *base;
void __iomem *ram;
base = musb->ctrl_base;
ram = DAVINCI_RXCPPI_STATERAM_OFFSET(
cppi) + base;
code = snprintf(buf, max,
" tx dma%d: "
"%08x %08x, %08x %08x; "
"%08x %08x %08x .. %08x\n",
cppi,
musb_readl(ram, 0 * 4),
musb_readl(ram, 1 * 4),
musb_readl(ram, 2 * 4),
musb_readl(ram, 3 * 4),
musb_readl(ram, 4 * 4),
musb_readl(ram, 5 * 4),
musb_readl(ram, 6 * 4),
musb_readl(ram, 7 * 4));
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
if (hw_ep == musb->control_ep
&& !list_empty(
&musb->control)) {
code = dump_queue(&musb->control,
buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
} else if (hw_ep == musb->bulk_ep
&& !list_empty(
&musb->out_bulk)) {
code = dump_queue(&musb->out_bulk,
buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
} else if (musb->periodic[epnum]) {
code = dump_qh(musb->periodic[epnum],
buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
}
}
#endif
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (is_peripheral_active(musb)) {
code = 0;
if (hw_ep->ep_in.desc || !epnum) {
code = dump_ep(&hw_ep->ep_in, buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
if (hw_ep->ep_out.desc) {
code = dump_ep(&hw_ep->ep_out, buf, max);
if (code <= 0)
break;
code = min(code, (int) max);
buf += code;
max -= code;
}
}
#endif
} while (0);
return buf - aBuffer;
}
/* Dump the current status and compile options.
* @param musb the device driver instance
* @param buffer where to dump the status; it must be big enough to hold the
* result otherwise "BAD THINGS HAPPENS(TM)".
*/
static int dump_header_stats(struct musb *musb, char *buffer)
{
int code, count = 0;
const void __iomem *mbase = musb->mregs;
*buffer = 0;
count = sprintf(buffer, "Status: %sHDRC, Mode=%s "
"(Power=%02x, DevCtl=%02x)\n",
(musb->is_multipoint ? "M" : ""), MUSB_MODE(musb),
musb_readb(mbase, MUSB_POWER),
musb_readb(mbase, MUSB_DEVCTL));
if (count <= 0)
return 0;
buffer += count;
code = sprintf(buffer, "OTG state: %s; %sactive\n",
otg_state_string(musb),
musb->is_active ? "" : "in");
if (code <= 0)
goto done;
buffer += code;
count += code;
code = sprintf(buffer,
"Options: "
#ifdef CONFIG_MUSB_PIO_ONLY
"pio"
#elif defined(CONFIG_USB_TI_CPPI_DMA)
"cppi-dma"
#elif defined(CONFIG_USB_INVENTRA_DMA)
"musb-dma"
#elif defined(CONFIG_USB_TUSB_OMAP_DMA)
"tusb-omap-dma"
#else
"?dma?"
#endif
", "
#ifdef CONFIG_USB_MUSB_OTG
"otg (peripheral+host)"
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
"peripheral"
#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
"host"
#endif
", debug=%d [eps=%d]\n",
debug,
musb->nr_endpoints);
if (code <= 0)
goto done;
count += code;
buffer += code;
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
code = sprintf(buffer, "Peripheral address: %02x\n",
musb_readb(musb->ctrl_base, MUSB_FADDR));
if (code <= 0)
goto done;
buffer += code;
count += code;
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD
code = sprintf(buffer, "Root port status: %08x\n",
musb->port1_status);
if (code <= 0)
goto done;
buffer += code;
count += code;
#endif
#ifdef CONFIG_ARCH_DAVINCI
code = sprintf(buffer,
"DaVinci: ctrl=%02x stat=%1x phy=%03x\n"
"\trndis=%05x auto=%04x intsrc=%08x intmsk=%08x"
"\n",
musb_readl(musb->ctrl_base, DAVINCI_USB_CTRL_REG),
musb_readl(musb->ctrl_base, DAVINCI_USB_STAT_REG),
__raw_readl((void __force __iomem *)
IO_ADDRESS(USBPHY_CTL_PADDR)),
musb_readl(musb->ctrl_base, DAVINCI_RNDIS_REG),
musb_readl(musb->ctrl_base, DAVINCI_AUTOREQ_REG),
musb_readl(musb->ctrl_base,
DAVINCI_USB_INT_SOURCE_REG),
musb_readl(musb->ctrl_base,
DAVINCI_USB_INT_MASK_REG));
if (code <= 0)
goto done;
count += code;
buffer += code;
#endif /* DAVINCI */
#ifdef CONFIG_USB_TUSB6010
code = sprintf(buffer,
"TUSB6010: devconf %08x, phy enable %08x drive %08x"
"\n\totg %03x timer %08x"
"\n\tprcm conf %08x mgmt %08x; int src %08x mask %08x"
"\n",
musb_readl(musb->ctrl_base, TUSB_DEV_CONF),
musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL_ENABLE),
musb_readl(musb->ctrl_base, TUSB_PHY_OTG_CTRL),
musb_readl(musb->ctrl_base, TUSB_DEV_OTG_STAT),
musb_readl(musb->ctrl_base, TUSB_DEV_OTG_TIMER),
musb_readl(musb->ctrl_base, TUSB_PRCM_CONF),
musb_readl(musb->ctrl_base, TUSB_PRCM_MNGMT),
musb_readl(musb->ctrl_base, TUSB_INT_SRC),
musb_readl(musb->ctrl_base, TUSB_INT_MASK));
if (code <= 0)
goto done;
count += code;
buffer += code;
#endif /* DAVINCI */
if (is_cppi_enabled() && musb->dma_controller) {
code = sprintf(buffer,
"CPPI: txcr=%d txsrc=%01x txena=%01x; "
"rxcr=%d rxsrc=%01x rxena=%01x "
"\n",
musb_readl(musb->ctrl_base,
DAVINCI_TXCPPI_CTRL_REG),
musb_readl(musb->ctrl_base,
DAVINCI_TXCPPI_RAW_REG),
musb_readl(musb->ctrl_base,
DAVINCI_TXCPPI_INTENAB_REG),
musb_readl(musb->ctrl_base,
DAVINCI_RXCPPI_CTRL_REG),
musb_readl(musb->ctrl_base,
DAVINCI_RXCPPI_RAW_REG),
musb_readl(musb->ctrl_base,
DAVINCI_RXCPPI_INTENAB_REG));
if (code <= 0)
goto done;
count += code;
buffer += code;
}
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (is_peripheral_enabled(musb)) {
code = sprintf(buffer, "Gadget driver: %s\n",
musb->gadget_driver
? musb->gadget_driver->driver.name
: "(none)");
if (code <= 0)
goto done;
count += code;
buffer += code;
}
#endif
done:
return count;
}
/* Write to ProcFS
*
* C soft-connect
* c soft-disconnect
* I enable HS
* i disable HS
* s stop session
* F force session (OTG-unfriendly)
* E rElinquish bus (OTG)
* H request host mode
* h cancel host request
* T start sending TEST_PACKET
* D<num> set/query the debug level
*/
static int musb_proc_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
char cmd;
u8 reg;
struct musb *musb = (struct musb *)data;
void __iomem *mbase = musb->mregs;
/* MOD_INC_USE_COUNT; */
if (unlikely(copy_from_user(&cmd, buffer, 1)))
return -EFAULT;
switch (cmd) {
case 'C':
if (mbase) {
reg = musb_readb(mbase, MUSB_POWER)
| MUSB_POWER_SOFTCONN;
musb_writeb(mbase, MUSB_POWER, reg);
}
break;
case 'c':
if (mbase) {
reg = musb_readb(mbase, MUSB_POWER)
& ~MUSB_POWER_SOFTCONN;
musb_writeb(mbase, MUSB_POWER, reg);
}
break;
case 'I':
if (mbase) {
reg = musb_readb(mbase, MUSB_POWER)
| MUSB_POWER_HSENAB;
musb_writeb(mbase, MUSB_POWER, reg);
}
break;
case 'i':
if (mbase) {
reg = musb_readb(mbase, MUSB_POWER)
& ~MUSB_POWER_HSENAB;
musb_writeb(mbase, MUSB_POWER, reg);
}
break;
case 'F':
reg = musb_readb(mbase, MUSB_DEVCTL);
reg |= MUSB_DEVCTL_SESSION;
musb_writeb(mbase, MUSB_DEVCTL, reg);
break;
case 'H':
if (mbase) {
reg = musb_readb(mbase, MUSB_DEVCTL);
reg |= MUSB_DEVCTL_HR;
musb_writeb(mbase, MUSB_DEVCTL, reg);
/* MUSB_HST_MODE( ((struct musb*)data) ); */
/* WARNING("Host Mode\n"); */
}
break;
case 'h':
if (mbase) {
reg = musb_readb(mbase, MUSB_DEVCTL);
reg &= ~MUSB_DEVCTL_HR;
musb_writeb(mbase, MUSB_DEVCTL, reg);
}
break;
case 'T':
if (mbase) {
musb_load_testpacket(musb);
musb_writeb(mbase, MUSB_TESTMODE,
MUSB_TEST_PACKET);
}
break;
#if (MUSB_DEBUG > 0)
/* set/read debug level */
case 'D':{
if (count > 1) {
char digits[8], *p = digits;
int i = 0, level = 0, sign = 1;
int len = min(count - 1, (unsigned long)8);
if (copy_from_user(&digits, &buffer[1], len))
return -EFAULT;
/* optional sign */
if (*p == '-') {
len -= 1;
sign = -sign;
p++;
}
/* read it */
while (i++ < len && *p > '0' && *p < '9') {
level = level * 10 + (*p - '0');
p++;
}
level *= sign;
DBG(1, "debug level %d\n", level);
debug = level;
}
}
break;
case '?':
INFO("?: you are seeing it\n");
INFO("C/c: soft connect enable/disable\n");
INFO("I/i: hispeed enable/disable\n");
INFO("F: force session start\n");
INFO("H: host mode\n");
INFO("T: start sending TEST_PACKET\n");
INFO("D: set/read dbug level\n");
break;
#endif
default:
ERR("Command %c not implemented\n", cmd);
break;
}
musb_platform_try_idle(musb, 0);
return count;
}
static int musb_proc_read(char *page, char **start,
off_t off, int count, int *eof, void *data)
{
char *buffer = page;
int code = 0;
unsigned long flags;
struct musb *musb = data;
unsigned epnum;
count -= off;
count -= 1; /* for NUL at end */
if (count <= 0)
return -EINVAL;
spin_lock_irqsave(&musb->lock, flags);
code = dump_header_stats(musb, buffer);
if (code > 0) {
buffer += code;
count -= code;
}
/* generate the report for the end points */
/* REVISIT ... not unless something's connected! */
for (epnum = 0; count >= 0 && epnum < musb->nr_endpoints;
epnum++) {
code = dump_end_info(musb, epnum, buffer, count);
if (code > 0) {
buffer += code;
count -= code;
}
}
musb_platform_try_idle(musb, 0);
spin_unlock_irqrestore(&musb->lock, flags);
*eof = 1;
return buffer - page;
}
void __devexit musb_debug_delete(char *name, struct musb *musb)
{
if (musb->proc_entry)
remove_proc_entry(name, NULL);
}
struct proc_dir_entry *__init
musb_debug_create(char *name, struct musb *data)
{
struct proc_dir_entry *pde;
/* FIXME convert everything to seq_file; then later, debugfs */
if (!name)
return NULL;
pde = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, NULL);
data->proc_entry = pde;
if (pde) {
pde->data = data;
/* pde->owner = THIS_MODULE; */
pde->read_proc = musb_proc_read;
pde->write_proc = musb_proc_write;
pde->size = 0;
pr_debug("Registered /proc/%s\n", name);
} else {
pr_debug("Cannot create a valid proc file entry");
}
return pde;
}

View File

@ -0,0 +1,300 @@
/*
* MUSB OTG driver register defines
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef __MUSB_REGS_H__
#define __MUSB_REGS_H__
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
/*
* Common USB registers
*/
#define MUSB_FADDR 0x00 /* 8-bit */
#define MUSB_POWER 0x01 /* 8-bit */
#define MUSB_INTRTX 0x02 /* 16-bit */
#define MUSB_INTRRX 0x04
#define MUSB_INTRTXE 0x06
#define MUSB_INTRRXE 0x08
#define MUSB_INTRUSB 0x0A /* 8 bit */
#define MUSB_INTRUSBE 0x0B /* 8 bit */
#define MUSB_FRAME 0x0C
#define MUSB_INDEX 0x0E /* 8 bit */
#define MUSB_TESTMODE 0x0F /* 8 bit */
/* Get offset for a given FIFO from musb->mregs */
#ifdef CONFIG_USB_TUSB6010
#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
#else
#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
#endif
/*
* Additional Control Registers
*/
#define MUSB_DEVCTL 0x60 /* 8 bit */
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */
#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */
/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */
#define MUSB_HWVERS 0x6C /* 8 bit */
#define MUSB_EPINFO 0x78 /* 8 bit */
#define MUSB_RAMINFO 0x79 /* 8 bit */
#define MUSB_LINKINFO 0x7a /* 8 bit */
#define MUSB_VPLEN 0x7b /* 8 bit */
#define MUSB_HS_EOF1 0x7c /* 8 bit */
#define MUSB_FS_EOF1 0x7d /* 8 bit */
#define MUSB_LS_EOF1 0x7e /* 8 bit */
/* Offsets to endpoint registers */
#define MUSB_TXMAXP 0x00
#define MUSB_TXCSR 0x02
#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */
#define MUSB_RXMAXP 0x04
#define MUSB_RXCSR 0x06
#define MUSB_RXCOUNT 0x08
#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */
#define MUSB_TXTYPE 0x0A
#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */
#define MUSB_TXINTERVAL 0x0B
#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */
#define MUSB_RXTYPE 0x0C
#define MUSB_RXINTERVAL 0x0D
#define MUSB_FIFOSIZE 0x0F
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
/* Offsets to endpoint registers in indexed model (using INDEX register) */
#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
(0x10 + (_offset))
/* Offsets to endpoint registers in flat models */
#define MUSB_FLAT_OFFSET(_epnum, _offset) \
(0x100 + (0x10*(_epnum)) + (_offset))
#ifdef CONFIG_USB_TUSB6010
/* TUSB6010 EP0 configuration register is special */
#define MUSB_TUSB_OFFSET(_epnum, _offset) \
(0x10 + _offset)
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
#endif
/* "bus control"/target registers, for host side multipoint (external hubs) */
#define MUSB_TXFUNCADDR 0x00
#define MUSB_TXHUBADDR 0x02
#define MUSB_TXHUBPORT 0x03
#define MUSB_RXFUNCADDR 0x04
#define MUSB_RXHUBADDR 0x06
#define MUSB_RXHUBPORT 0x07
#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \
(0x80 + (8*(_epnum)) + (_offset))
/*
* MUSB Register bits
*/
/* POWER */
#define MUSB_POWER_ISOUPDATE 0x80
#define MUSB_POWER_SOFTCONN 0x40
#define MUSB_POWER_HSENAB 0x20
#define MUSB_POWER_HSMODE 0x10
#define MUSB_POWER_RESET 0x08
#define MUSB_POWER_RESUME 0x04
#define MUSB_POWER_SUSPENDM 0x02
#define MUSB_POWER_ENSUSPEND 0x01
/* INTRUSB */
#define MUSB_INTR_SUSPEND 0x01
#define MUSB_INTR_RESUME 0x02
#define MUSB_INTR_RESET 0x04
#define MUSB_INTR_BABBLE 0x04
#define MUSB_INTR_SOF 0x08
#define MUSB_INTR_CONNECT 0x10
#define MUSB_INTR_DISCONNECT 0x20
#define MUSB_INTR_SESSREQ 0x40
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
/* DEVCTL */
#define MUSB_DEVCTL_BDEVICE 0x80
#define MUSB_DEVCTL_FSDEV 0x40
#define MUSB_DEVCTL_LSDEV 0x20
#define MUSB_DEVCTL_VBUS 0x18
#define MUSB_DEVCTL_VBUS_SHIFT 3
#define MUSB_DEVCTL_HM 0x04
#define MUSB_DEVCTL_HR 0x02
#define MUSB_DEVCTL_SESSION 0x01
/* TESTMODE */
#define MUSB_TEST_FORCE_HOST 0x80
#define MUSB_TEST_FIFO_ACCESS 0x40
#define MUSB_TEST_FORCE_FS 0x20
#define MUSB_TEST_FORCE_HS 0x10
#define MUSB_TEST_PACKET 0x08
#define MUSB_TEST_K 0x04
#define MUSB_TEST_J 0x02
#define MUSB_TEST_SE0_NAK 0x01
/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */
#define MUSB_FIFOSZ_DPB 0x10
/* Allocation size (8, 16, 32, ... 4096) */
#define MUSB_FIFOSZ_SIZE 0x0f
/* CSR0 */
#define MUSB_CSR0_FLUSHFIFO 0x0100
#define MUSB_CSR0_TXPKTRDY 0x0002
#define MUSB_CSR0_RXPKTRDY 0x0001
/* CSR0 in Peripheral mode */
#define MUSB_CSR0_P_SVDSETUPEND 0x0080
#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040
#define MUSB_CSR0_P_SENDSTALL 0x0020
#define MUSB_CSR0_P_SETUPEND 0x0010
#define MUSB_CSR0_P_DATAEND 0x0008
#define MUSB_CSR0_P_SENTSTALL 0x0004
/* CSR0 in Host mode */
#define MUSB_CSR0_H_DIS_PING 0x0800
#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */
#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */
#define MUSB_CSR0_H_NAKTIMEOUT 0x0080
#define MUSB_CSR0_H_STATUSPKT 0x0040
#define MUSB_CSR0_H_REQPKT 0x0020
#define MUSB_CSR0_H_ERROR 0x0010
#define MUSB_CSR0_H_SETUPPKT 0x0008
#define MUSB_CSR0_H_RXSTALL 0x0004
/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_CSR0_P_WZC_BITS \
(MUSB_CSR0_P_SENTSTALL)
#define MUSB_CSR0_H_WZC_BITS \
(MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \
| MUSB_CSR0_RXPKTRDY)
/* TxType/RxType */
#define MUSB_TYPE_SPEED 0xc0
#define MUSB_TYPE_SPEED_SHIFT 6
#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */
#define MUSB_TYPE_PROTO_SHIFT 4
#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */
/* CONFIGDATA */
#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */
#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */
#define MUSB_CONFIGDATA_BIGENDIAN 0x20
#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */
#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */
/* TXCSR in Peripheral and Host mode */
#define MUSB_TXCSR_AUTOSET 0x8000
#define MUSB_TXCSR_MODE 0x2000
#define MUSB_TXCSR_DMAENAB 0x1000
#define MUSB_TXCSR_FRCDATATOG 0x0800
#define MUSB_TXCSR_DMAMODE 0x0400
#define MUSB_TXCSR_CLRDATATOG 0x0040
#define MUSB_TXCSR_FLUSHFIFO 0x0008
#define MUSB_TXCSR_FIFONOTEMPTY 0x0002
#define MUSB_TXCSR_TXPKTRDY 0x0001
/* TXCSR in Peripheral mode */
#define MUSB_TXCSR_P_ISO 0x4000
#define MUSB_TXCSR_P_INCOMPTX 0x0080
#define MUSB_TXCSR_P_SENTSTALL 0x0020
#define MUSB_TXCSR_P_SENDSTALL 0x0010
#define MUSB_TXCSR_P_UNDERRUN 0x0004
/* TXCSR in Host mode */
#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200
#define MUSB_TXCSR_H_DATATOGGLE 0x0100
#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080
#define MUSB_TXCSR_H_RXSTALL 0x0020
#define MUSB_TXCSR_H_ERROR 0x0004
/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_TXCSR_P_WZC_BITS \
(MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \
| MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY)
#define MUSB_TXCSR_H_WZC_BITS \
(MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \
| MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY)
/* RXCSR in Peripheral and Host mode */
#define MUSB_RXCSR_AUTOCLEAR 0x8000
#define MUSB_RXCSR_DMAENAB 0x2000
#define MUSB_RXCSR_DISNYET 0x1000
#define MUSB_RXCSR_PID_ERR 0x1000
#define MUSB_RXCSR_DMAMODE 0x0800
#define MUSB_RXCSR_INCOMPRX 0x0100
#define MUSB_RXCSR_CLRDATATOG 0x0080
#define MUSB_RXCSR_FLUSHFIFO 0x0010
#define MUSB_RXCSR_DATAERROR 0x0008
#define MUSB_RXCSR_FIFOFULL 0x0002
#define MUSB_RXCSR_RXPKTRDY 0x0001
/* RXCSR in Peripheral mode */
#define MUSB_RXCSR_P_ISO 0x4000
#define MUSB_RXCSR_P_SENTSTALL 0x0040
#define MUSB_RXCSR_P_SENDSTALL 0x0020
#define MUSB_RXCSR_P_OVERRUN 0x0004
/* RXCSR in Host mode */
#define MUSB_RXCSR_H_AUTOREQ 0x4000
#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400
#define MUSB_RXCSR_H_DATATOGGLE 0x0200
#define MUSB_RXCSR_H_RXSTALL 0x0040
#define MUSB_RXCSR_H_REQPKT 0x0020
#define MUSB_RXCSR_H_ERROR 0x0004
/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_RXCSR_P_WZC_BITS \
(MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \
| MUSB_RXCSR_RXPKTRDY)
#define MUSB_RXCSR_H_WZC_BITS \
(MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \
| MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY)
/* HUBADDR */
#define MUSB_HUBADDR_MULTI_TT 0x80
#endif /* __MUSB_REGS_H__ */

View File

@ -0,0 +1,425 @@
/*
* MUSB OTG driver virtual root hub support
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2006 by Texas Instruments
* Copyright (C) 2006-2007 Nokia Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <asm/unaligned.h>
#include "musb_core.h"
static void musb_port_suspend(struct musb *musb, bool do_suspend)
{
u8 power;
void __iomem *mbase = musb->mregs;
if (!is_host_active(musb))
return;
/* NOTE: this doesn't necessarily put PHY into low power mode,
* turning off its clock; that's a function of PHY integration and
* MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect
* SE0 changing to connect (J) or wakeup (K) states.
*/
power = musb_readb(mbase, MUSB_POWER);
if (do_suspend) {
int retries = 10000;
power &= ~MUSB_POWER_RESUME;
power |= MUSB_POWER_SUSPENDM;
musb_writeb(mbase, MUSB_POWER, power);
/* Needed for OPT A tests */
power = musb_readb(mbase, MUSB_POWER);
while (power & MUSB_POWER_SUSPENDM) {
power = musb_readb(mbase, MUSB_POWER);
if (retries-- < 1)
break;
}
DBG(3, "Root port suspended, power %02x\n", power);
musb->port1_status |= USB_PORT_STAT_SUSPEND;
switch (musb->xceiv.state) {
case OTG_STATE_A_HOST:
musb->xceiv.state = OTG_STATE_A_SUSPEND;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv.host->b_hnp_enable;
musb_platform_try_idle(musb, 0);
break;
#ifdef CONFIG_USB_MUSB_OTG
case OTG_STATE_B_HOST:
musb->xceiv.state = OTG_STATE_B_WAIT_ACON;
musb->is_active = is_otg_enabled(musb)
&& musb->xceiv.host->b_hnp_enable;
musb_platform_try_idle(musb, 0);
break;
#endif
default:
DBG(1, "bogus rh suspend? %s\n",
otg_state_string(musb));
}
} else if (power & MUSB_POWER_SUSPENDM) {
power &= ~MUSB_POWER_SUSPENDM;
power |= MUSB_POWER_RESUME;
musb_writeb(mbase, MUSB_POWER, power);
DBG(3, "Root port resuming, power %02x\n", power);
/* later, GetPortStatus will stop RESUME signaling */
musb->port1_status |= MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies + msecs_to_jiffies(20);
}
}
static void musb_port_reset(struct musb *musb, bool do_reset)
{
u8 power;
void __iomem *mbase = musb->mregs;
#ifdef CONFIG_USB_MUSB_OTG
if (musb->xceiv.state == OTG_STATE_B_IDLE) {
DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n");
musb->port1_status &= ~USB_PORT_STAT_RESET;
return;
}
#endif
if (!is_host_active(musb))
return;
/* NOTE: caller guarantees it will turn off the reset when
* the appropriate amount of time has passed
*/
power = musb_readb(mbase, MUSB_POWER);
if (do_reset) {
/*
* If RESUME is set, we must make sure it stays minimum 20 ms.
* Then we must clear RESUME and wait a bit to let musb start
* generating SOFs. If we don't do this, OPT HS A 6.8 tests
* fail with "Error! Did not receive an SOF before suspend
* detected".
*/
if (power & MUSB_POWER_RESUME) {
while (time_before(jiffies, musb->rh_timer))
msleep(1);
musb_writeb(mbase, MUSB_POWER,
power & ~MUSB_POWER_RESUME);
msleep(1);
}
musb->ignore_disconnect = true;
power &= 0xf0;
musb_writeb(mbase, MUSB_POWER,
power | MUSB_POWER_RESET);
musb->port1_status |= USB_PORT_STAT_RESET;
musb->port1_status &= ~USB_PORT_STAT_ENABLE;
musb->rh_timer = jiffies + msecs_to_jiffies(50);
} else {
DBG(4, "root port reset stopped\n");
musb_writeb(mbase, MUSB_POWER,
power & ~MUSB_POWER_RESET);
musb->ignore_disconnect = false;
power = musb_readb(mbase, MUSB_POWER);
if (power & MUSB_POWER_HSMODE) {
DBG(4, "high-speed device connected\n");
musb->port1_status |= USB_PORT_STAT_HIGH_SPEED;
}
musb->port1_status &= ~USB_PORT_STAT_RESET;
musb->port1_status |= USB_PORT_STAT_ENABLE
| (USB_PORT_STAT_C_RESET << 16)
| (USB_PORT_STAT_C_ENABLE << 16);
usb_hcd_poll_rh_status(musb_to_hcd(musb));
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
}
}
void musb_root_disconnect(struct musb *musb)
{
musb->port1_status = (1 << USB_PORT_FEAT_POWER)
| (1 << USB_PORT_FEAT_C_CONNECTION);
usb_hcd_poll_rh_status(musb_to_hcd(musb));
musb->is_active = 0;
switch (musb->xceiv.state) {
case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
musb->is_active = 0;
break;
case OTG_STATE_A_WAIT_VFALL:
musb->xceiv.state = OTG_STATE_B_IDLE;
break;
default:
DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
}
}
/*---------------------------------------------------------------------*/
/* Caller may or may not hold musb->lock */
int musb_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct musb *musb = hcd_to_musb(hcd);
int retval = 0;
/* called in_irq() via usb_hcd_poll_rh_status() */
if (musb->port1_status & 0xffff0000) {
*buf = 0x02;
retval = 1;
}
return retval;
}
int musb_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength)
{
struct musb *musb = hcd_to_musb(hcd);
u32 temp;
int retval = 0;
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
spin_unlock_irqrestore(&musb->lock, flags);
return -ESHUTDOWN;
}
/* hub features: always zero, setting is a NOP
* port features: reported, sometimes updated when host is active
* no indicators
*/
switch (typeReq) {
case ClearHubFeature:
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case ClearPortFeature:
if ((wIndex & 0xff) != 1)
goto error;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
break;
case USB_PORT_FEAT_SUSPEND:
musb_port_suspend(musb, false);
break;
case USB_PORT_FEAT_POWER:
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
musb_set_vbus(musb, 0);
break;
case USB_PORT_FEAT_C_CONNECTION:
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_OVER_CURRENT:
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_SUSPEND:
break;
default:
goto error;
}
DBG(5, "clear feature %d\n", wValue);
musb->port1_status &= ~(1 << wValue);
break;
case GetHubDescriptor:
{
struct usb_hub_descriptor *desc = (void *)buf;
desc->bDescLength = 9;
desc->bDescriptorType = 0x29;
desc->bNbrPorts = 1;
desc->wHubCharacteristics = __constant_cpu_to_le16(
0x0001 /* per-port power switching */
| 0x0010 /* no overcurrent reporting */
);
desc->bPwrOn2PwrGood = 5; /* msec/2 */
desc->bHubContrCurrent = 0;
/* workaround bogus struct definition */
desc->DeviceRemovable[0] = 0x02; /* port 1 */
desc->DeviceRemovable[1] = 0xff;
}
break;
case GetHubStatus:
temp = 0;
*(__le32 *) buf = cpu_to_le32(temp);
break;
case GetPortStatus:
if (wIndex != 1)
goto error;
/* finish RESET signaling? */
if ((musb->port1_status & USB_PORT_STAT_RESET)
&& time_after_eq(jiffies, musb->rh_timer))
musb_port_reset(musb, false);
/* finish RESUME signaling? */
if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
&& time_after_eq(jiffies, musb->rh_timer)) {
u8 power;
power = musb_readb(musb->mregs, MUSB_POWER);
power &= ~MUSB_POWER_RESUME;
DBG(4, "root port resume stopped, power %02x\n",
power);
musb_writeb(musb->mregs, MUSB_POWER, power);
/* ISSUE: DaVinci (RTL 1.300) disconnects after
* resume of high speed peripherals (but not full
* speed ones).
*/
musb->is_active = 1;
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
| MUSB_PORT_STAT_RESUME);
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb_to_hcd(musb));
/* NOTE: it might really be A_WAIT_BCON ... */
musb->xceiv.state = OTG_STATE_A_HOST;
}
put_unaligned(cpu_to_le32(musb->port1_status
& ~MUSB_PORT_STAT_RESUME),
(__le32 *) buf);
/* port change status is more interesting */
DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n",
musb->port1_status);
break;
case SetPortFeature:
if ((wIndex & 0xff) != 1)
goto error;
switch (wValue) {
case USB_PORT_FEAT_POWER:
/* NOTE: this controller has a strange state machine
* that involves "requesting sessions" according to
* magic side effects from incompletely-described
* rules about startup...
*
* This call is what really starts the host mode; be
* very careful about side effects if you reorder any
* initialization logic, e.g. for OTG, or change any
* logic relating to VBUS power-up.
*/
if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
musb_start(musb);
break;
case USB_PORT_FEAT_RESET:
musb_port_reset(musb, true);
break;
case USB_PORT_FEAT_SUSPEND:
musb_port_suspend(musb, true);
break;
case USB_PORT_FEAT_TEST:
if (unlikely(is_host_active(musb)))
goto error;
wIndex >>= 8;
switch (wIndex) {
case 1:
pr_debug("TEST_J\n");
temp = MUSB_TEST_J;
break;
case 2:
pr_debug("TEST_K\n");
temp = MUSB_TEST_K;
break;
case 3:
pr_debug("TEST_SE0_NAK\n");
temp = MUSB_TEST_SE0_NAK;
break;
case 4:
pr_debug("TEST_PACKET\n");
temp = MUSB_TEST_PACKET;
musb_load_testpacket(musb);
break;
case 5:
pr_debug("TEST_FORCE_ENABLE\n");
temp = MUSB_TEST_FORCE_HOST
| MUSB_TEST_FORCE_HS;
musb_writeb(musb->mregs, MUSB_DEVCTL,
MUSB_DEVCTL_SESSION);
break;
case 6:
pr_debug("TEST_FIFO_ACCESS\n");
temp = MUSB_TEST_FIFO_ACCESS;
break;
default:
goto error;
}
musb_writeb(musb->mregs, MUSB_TESTMODE, temp);
break;
default:
goto error;
}
DBG(5, "set feature %d\n", wValue);
musb->port1_status |= 1 << wValue;
break;
default:
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
spin_unlock_irqrestore(&musb->lock, flags);
return retval;
}

View File

@ -0,0 +1,433 @@
/*
* MUSB OTG driver - support for Mentor's DMA controller
*
* Copyright 2005 Mentor Graphics Corporation
* Copyright (C) 2005-2007 by Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "musb_core.h"
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
#include "omap2430.h"
#endif
#define MUSB_HSDMA_BASE 0x200
#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
#define MUSB_HSDMA_CONTROL 0x4
#define MUSB_HSDMA_ADDRESS 0x8
#define MUSB_HSDMA_COUNT 0xc
#define MUSB_HSDMA_CHANNEL_OFFSET(_bChannel, _offset) \
(MUSB_HSDMA_BASE + (_bChannel << 4) + _offset)
/* control register (16-bit): */
#define MUSB_HSDMA_ENABLE_SHIFT 0
#define MUSB_HSDMA_TRANSMIT_SHIFT 1
#define MUSB_HSDMA_MODE1_SHIFT 2
#define MUSB_HSDMA_IRQENABLE_SHIFT 3
#define MUSB_HSDMA_ENDPOINT_SHIFT 4
#define MUSB_HSDMA_BUSERROR_SHIFT 8
#define MUSB_HSDMA_BURSTMODE_SHIFT 9
#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT)
#define MUSB_HSDMA_BURSTMODE_UNSPEC 0
#define MUSB_HSDMA_BURSTMODE_INCR4 1
#define MUSB_HSDMA_BURSTMODE_INCR8 2
#define MUSB_HSDMA_BURSTMODE_INCR16 3
#define MUSB_HSDMA_CHANNELS 8
struct musb_dma_controller;
struct musb_dma_channel {
struct dma_channel Channel;
struct musb_dma_controller *controller;
u32 dwStartAddress;
u32 len;
u16 wMaxPacketSize;
u8 bIndex;
u8 epnum;
u8 transmit;
};
struct musb_dma_controller {
struct dma_controller Controller;
struct musb_dma_channel aChannel[MUSB_HSDMA_CHANNELS];
void *pDmaPrivate;
void __iomem *pCoreBase;
u8 bChannelCount;
u8 bmUsedChannels;
u8 irq;
};
static int dma_controller_start(struct dma_controller *c)
{
/* nothing to do */
return 0;
}
static void dma_channel_release(struct dma_channel *pChannel);
static int dma_controller_stop(struct dma_controller *c)
{
struct musb_dma_controller *controller =
container_of(c, struct musb_dma_controller, Controller);
struct musb *musb = (struct musb *) controller->pDmaPrivate;
struct dma_channel *pChannel;
u8 bBit;
if (controller->bmUsedChannels != 0) {
dev_err(musb->controller,
"Stopping DMA controller while channel active\n");
for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) {
if (controller->bmUsedChannels & (1 << bBit)) {
pChannel = &controller->aChannel[bBit].Channel;
dma_channel_release(pChannel);
if (!controller->bmUsedChannels)
break;
}
}
}
return 0;
}
static struct dma_channel *dma_channel_allocate(struct dma_controller *c,
struct musb_hw_ep *hw_ep, u8 transmit)
{
u8 bBit;
struct dma_channel *pChannel = NULL;
struct musb_dma_channel *pImplChannel = NULL;
struct musb_dma_controller *controller =
container_of(c, struct musb_dma_controller, Controller);
for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) {
if (!(controller->bmUsedChannels & (1 << bBit))) {
controller->bmUsedChannels |= (1 << bBit);
pImplChannel = &(controller->aChannel[bBit]);
pImplChannel->controller = controller;
pImplChannel->bIndex = bBit;
pImplChannel->epnum = hw_ep->epnum;
pImplChannel->transmit = transmit;
pChannel = &(pImplChannel->Channel);
pChannel->private_data = pImplChannel;
pChannel->status = MUSB_DMA_STATUS_FREE;
pChannel->max_len = 0x10000;
/* Tx => mode 1; Rx => mode 0 */
pChannel->desired_mode = transmit;
pChannel->actual_len = 0;
break;
}
}
return pChannel;
}
static void dma_channel_release(struct dma_channel *pChannel)
{
struct musb_dma_channel *pImplChannel =
(struct musb_dma_channel *) pChannel->private_data;
pChannel->actual_len = 0;
pImplChannel->dwStartAddress = 0;
pImplChannel->len = 0;
pImplChannel->controller->bmUsedChannels &=
~(1 << pImplChannel->bIndex);
pChannel->status = MUSB_DMA_STATUS_UNKNOWN;
}
static void configure_channel(struct dma_channel *pChannel,
u16 packet_sz, u8 mode,
dma_addr_t dma_addr, u32 len)
{
struct musb_dma_channel *pImplChannel =
(struct musb_dma_channel *) pChannel->private_data;
struct musb_dma_controller *controller = pImplChannel->controller;
void __iomem *mbase = controller->pCoreBase;
u8 bChannel = pImplChannel->bIndex;
u16 csr = 0;
DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n",
pChannel, packet_sz, dma_addr, len, mode);
if (mode) {
csr |= 1 << MUSB_HSDMA_MODE1_SHIFT;
BUG_ON(len < packet_sz);
if (packet_sz >= 64) {
csr |= MUSB_HSDMA_BURSTMODE_INCR16
<< MUSB_HSDMA_BURSTMODE_SHIFT;
} else if (packet_sz >= 32) {
csr |= MUSB_HSDMA_BURSTMODE_INCR8
<< MUSB_HSDMA_BURSTMODE_SHIFT;
} else if (packet_sz >= 16) {
csr |= MUSB_HSDMA_BURSTMODE_INCR4
<< MUSB_HSDMA_BURSTMODE_SHIFT;
}
}
csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT)
| (1 << MUSB_HSDMA_ENABLE_SHIFT)
| (1 << MUSB_HSDMA_IRQENABLE_SHIFT)
| (pImplChannel->transmit
? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)
: 0);
/* address/count */
musb_writel(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
dma_addr);
musb_writel(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
len);
/* control (this should start things) */
musb_writew(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
csr);
}
static int dma_channel_program(struct dma_channel *pChannel,
u16 packet_sz, u8 mode,
dma_addr_t dma_addr, u32 len)
{
struct musb_dma_channel *pImplChannel =
(struct musb_dma_channel *) pChannel->private_data;
DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n",
pImplChannel->epnum,
pImplChannel->transmit ? "Tx" : "Rx",
packet_sz, dma_addr, len, mode);
BUG_ON(pChannel->status == MUSB_DMA_STATUS_UNKNOWN ||
pChannel->status == MUSB_DMA_STATUS_BUSY);
pChannel->actual_len = 0;
pImplChannel->dwStartAddress = dma_addr;
pImplChannel->len = len;
pImplChannel->wMaxPacketSize = packet_sz;
pChannel->status = MUSB_DMA_STATUS_BUSY;
if ((mode == 1) && (len >= packet_sz))
configure_channel(pChannel, packet_sz, 1, dma_addr, len);
else
configure_channel(pChannel, packet_sz, 0, dma_addr, len);
return true;
}
static int dma_channel_abort(struct dma_channel *pChannel)
{
struct musb_dma_channel *pImplChannel =
(struct musb_dma_channel *) pChannel->private_data;
u8 bChannel = pImplChannel->bIndex;
void __iomem *mbase = pImplChannel->controller->pCoreBase;
u16 csr;
if (pChannel->status == MUSB_DMA_STATUS_BUSY) {
if (pImplChannel->transmit) {
csr = musb_readw(mbase,
MUSB_EP_OFFSET(pImplChannel->epnum,
MUSB_TXCSR));
csr &= ~(MUSB_TXCSR_AUTOSET |
MUSB_TXCSR_DMAENAB |
MUSB_TXCSR_DMAMODE);
musb_writew(mbase,
MUSB_EP_OFFSET(pImplChannel->epnum,
MUSB_TXCSR),
csr);
} else {
csr = musb_readw(mbase,
MUSB_EP_OFFSET(pImplChannel->epnum,
MUSB_RXCSR));
csr &= ~(MUSB_RXCSR_AUTOCLEAR |
MUSB_RXCSR_DMAENAB |
MUSB_RXCSR_DMAMODE);
musb_writew(mbase,
MUSB_EP_OFFSET(pImplChannel->epnum,
MUSB_RXCSR),
csr);
}
musb_writew(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL),
0);
musb_writel(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS),
0);
musb_writel(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT),
0);
pChannel->status = MUSB_DMA_STATUS_FREE;
}
return 0;
}
static irqreturn_t dma_controller_irq(int irq, void *private_data)
{
struct musb_dma_controller *controller =
(struct musb_dma_controller *)private_data;
struct musb_dma_channel *pImplChannel;
struct musb *musb = controller->pDmaPrivate;
void __iomem *mbase = controller->pCoreBase;
struct dma_channel *pChannel;
u8 bChannel;
u16 csr;
u32 dwAddress;
u8 int_hsdma;
irqreturn_t retval = IRQ_NONE;
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
if (!int_hsdma)
goto done;
for (bChannel = 0; bChannel < MUSB_HSDMA_CHANNELS; bChannel++) {
if (int_hsdma & (1 << bChannel)) {
pImplChannel = (struct musb_dma_channel *)
&(controller->aChannel[bChannel]);
pChannel = &pImplChannel->Channel;
csr = musb_readw(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(bChannel,
MUSB_HSDMA_CONTROL));
if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT))
pImplChannel->Channel.status =
MUSB_DMA_STATUS_BUS_ABORT;
else {
u8 devctl;
dwAddress = musb_readl(mbase,
MUSB_HSDMA_CHANNEL_OFFSET(
bChannel,
MUSB_HSDMA_ADDRESS));
pChannel->actual_len = dwAddress
- pImplChannel->dwStartAddress;
DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n",
pChannel, pImplChannel->dwStartAddress,
dwAddress, pChannel->actual_len,
pImplChannel->len,
(pChannel->actual_len
< pImplChannel->len) ?
"=> reconfig 0" : "=> complete");
devctl = musb_readb(mbase, MUSB_DEVCTL);
pChannel->status = MUSB_DMA_STATUS_FREE;
/* completed */
if ((devctl & MUSB_DEVCTL_HM)
&& (pImplChannel->transmit)
&& ((pChannel->desired_mode == 0)
|| (pChannel->actual_len &
(pImplChannel->wMaxPacketSize - 1)))
) {
/* Send out the packet */
musb_ep_select(mbase,
pImplChannel->epnum);
musb_writew(mbase, MUSB_EP_OFFSET(
pImplChannel->epnum,
MUSB_TXCSR),
MUSB_TXCSR_TXPKTRDY);
} else
musb_dma_completion(
musb,
pImplChannel->epnum,
pImplChannel->transmit);
}
}
}
retval = IRQ_HANDLED;
done:
spin_unlock_irqrestore(&musb->lock, flags);
return retval;
}
void dma_controller_destroy(struct dma_controller *c)
{
struct musb_dma_controller *controller;
controller = container_of(c, struct musb_dma_controller, Controller);
if (!controller)
return;
if (controller->irq)
free_irq(controller->irq, c);
kfree(controller);
}
struct dma_controller *__init
dma_controller_create(struct musb *musb, void __iomem *pCoreBase)
{
struct musb_dma_controller *controller;
struct device *dev = musb->controller;
struct platform_device *pdev = to_platform_device(dev);
int irq = platform_get_irq(pdev, 1);
if (irq == 0) {
dev_err(dev, "No DMA interrupt line!\n");
return NULL;
}
controller = kzalloc(sizeof(struct musb_dma_controller), GFP_KERNEL);
if (!controller)
return NULL;
controller->bChannelCount = MUSB_HSDMA_CHANNELS;
controller->pDmaPrivate = musb;
controller->pCoreBase = pCoreBase;
controller->Controller.start = dma_controller_start;
controller->Controller.stop = dma_controller_stop;
controller->Controller.channel_alloc = dma_channel_allocate;
controller->Controller.channel_release = dma_channel_release;
controller->Controller.channel_program = dma_channel_program;
controller->Controller.channel_abort = dma_channel_abort;
if (request_irq(irq, dma_controller_irq, IRQF_DISABLED,
musb->controller->bus_id, &controller->Controller)) {
dev_err(dev, "request_irq %d failed!\n", irq);
dma_controller_destroy(&controller->Controller);
return NULL;
}
controller->irq = irq;
return &controller->Controller;
}

324
drivers/usb/musb/omap2430.c Normal file
View File

@ -0,0 +1,324 @@
/*
* Copyright (C) 2005-2007 by Texas Instruments
* Some code has been taken from tusb6010.c
* Copyrights for that are attributable to:
* Copyright (C) 2006 Nokia Corporation
* Jarkko Nikula <jarkko.nikula@nokia.com>
* Tony Lindgren <tony@atomide.com>
*
* This file is part of the Inventra Controller Driver for Linux.
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*
* The Inventra Controller Driver for Linux is distributed in
* the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with The Inventra Controller Driver for Linux ; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
#include <asm/arch/mux.h>
#include "musb_core.h"
#include "omap2430.h"
#ifdef CONFIG_ARCH_OMAP3430
#define get_cpu_rev() 2
#endif
#define MUSB_TIMEOUT_A_WAIT_BCON 1100
static struct timer_list musb_idle_timer;
static void musb_do_idle(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
unsigned long flags;
u8 power;
u8 devctl;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv.state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv.state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
musb->xceiv.state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_SUSPEND:
/* finish RESUME signaling? */
if (musb->port1_status & MUSB_PORT_STAT_RESUME) {
power = musb_readb(musb->mregs, MUSB_POWER);
power &= ~MUSB_POWER_RESUME;
DBG(1, "root port resume stopped, power %02x\n", power);
musb_writeb(musb->mregs, MUSB_POWER, power);
musb->is_active = 1;
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
| MUSB_PORT_STAT_RESUME);
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb_to_hcd(musb));
/* NOTE: it might really be A_WAIT_BCON ... */
musb->xceiv.state = OTG_STATE_A_HOST;
}
break;
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD
case OTG_STATE_A_HOST:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
musb->xceiv.state = OTG_STATE_B_IDLE;
else
musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
#endif
default:
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
}
void musb_platform_try_idle(struct musb *musb, unsigned long timeout)
{
unsigned long default_timeout = jiffies + msecs_to_jiffies(3);
static unsigned long last_timer;
if (timeout == 0)
timeout = default_timeout;
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || ((musb->a_wait_bcon == 0)
&& (musb->xceiv.state == OTG_STATE_A_WAIT_BCON))) {
DBG(4, "%s active, deleting timer\n", otg_state_string(musb));
del_timer(&musb_idle_timer);
last_timer = jiffies;
return;
}
if (time_after(last_timer, timeout)) {
if (!timer_pending(&musb_idle_timer))
last_timer = timeout;
else {
DBG(4, "Longer idle timer already pending, ignoring\n");
return;
}
}
last_timer = timeout;
DBG(4, "%s inactive, for idle timer for %lu ms\n",
otg_state_string(musb),
(unsigned long)jiffies_to_msecs(timeout - jiffies));
mod_timer(&musb_idle_timer, timeout);
}
void musb_platform_enable(struct musb *musb)
{
}
void musb_platform_disable(struct musb *musb)
{
}
static void omap_vbus_power(struct musb *musb, int is_on, int sleeping)
{
}
static void omap_set_vbus(struct musb *musb, int is_on)
{
u8 devctl;
/* HDRC controls CPEN, but beware current surges during device
* connect. They can trigger transient overcurrent conditions
* that must be ignored.
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (is_on) {
musb->is_active = 1;
musb->xceiv.default_a = 1;
musb->xceiv.state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
} else {
musb->is_active = 0;
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
* jumping right to B_IDLE...
*/
musb->xceiv.default_a = 0;
musb->xceiv.state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
MUSB_DEV_MODE(musb);
}
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
DBG(1, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
otg_state_string(musb),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
static int omap_set_power(struct otg_transceiver *x, unsigned mA)
{
return 0;
}
static int musb_platform_resume(struct musb *musb);
void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
{
u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
switch (musb_mode) {
case MUSB_HOST:
otg_set_host(&musb->xceiv, musb->xceiv.host);
break;
case MUSB_PERIPHERAL:
otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget);
break;
case MUSB_OTG:
break;
}
}
int __init musb_platform_init(struct musb *musb)
{
u32 l;
#if defined(CONFIG_ARCH_OMAP2430)
omap_cfg_reg(AE5_2430_USB0HS_STP);
#endif
musb_platform_resume(musb);
l = omap_readl(OTG_SYSCONFIG);
l &= ~ENABLEWAKEUP; /* disable wakeup */
l &= ~NOSTDBY; /* remove possible nostdby */
l |= SMARTSTDBY; /* enable smart standby */
l &= ~AUTOIDLE; /* disable auto idle */
l &= ~NOIDLE; /* remove possible noidle */
l |= SMARTIDLE; /* enable smart idle */
l |= AUTOIDLE; /* enable auto idle */
omap_writel(l, OTG_SYSCONFIG);
l = omap_readl(OTG_INTERFSEL);
l |= ULPI_12PIN;
omap_writel(l, OTG_INTERFSEL);
pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, "
"sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n",
omap_readl(OTG_REVISION), omap_readl(OTG_SYSCONFIG),
omap_readl(OTG_SYSSTATUS), omap_readl(OTG_INTERFSEL),
omap_readl(OTG_SIMENABLE));
omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1);
if (is_host_enabled(musb))
musb->board_set_vbus = omap_set_vbus;
if (is_peripheral_enabled(musb))
musb->xceiv.set_power = omap_set_power;
musb->a_wait_bcon = MUSB_TIMEOUT_A_WAIT_BCON;
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
return 0;
}
int musb_platform_suspend(struct musb *musb)
{
u32 l;
if (!musb->clock)
return 0;
/* in any role */
l = omap_readl(OTG_FORCESTDBY);
l |= ENABLEFORCE; /* enable MSTANDBY */
omap_writel(l, OTG_FORCESTDBY);
l = omap_readl(OTG_SYSCONFIG);
l |= ENABLEWAKEUP; /* enable wakeup */
omap_writel(l, OTG_SYSCONFIG);
if (musb->xceiv.set_suspend)
musb->xceiv.set_suspend(&musb->xceiv, 1);
if (musb->set_clock)
musb->set_clock(musb->clock, 0);
else
clk_disable(musb->clock);
return 0;
}
static int musb_platform_resume(struct musb *musb)
{
u32 l;
if (!musb->clock)
return 0;
if (musb->xceiv.set_suspend)
musb->xceiv.set_suspend(&musb->xceiv, 0);
if (musb->set_clock)
musb->set_clock(musb->clock, 1);
else
clk_enable(musb->clock);
l = omap_readl(OTG_SYSCONFIG);
l &= ~ENABLEWAKEUP; /* disable wakeup */
omap_writel(l, OTG_SYSCONFIG);
l = omap_readl(OTG_FORCESTDBY);
l &= ~ENABLEFORCE; /* disable MSTANDBY */
omap_writel(l, OTG_FORCESTDBY);
return 0;
}
int musb_platform_exit(struct musb *musb)
{
omap_vbus_power(musb, 0 /*off*/, 1);
musb_platform_suspend(musb);
clk_put(musb->clock);
musb->clock = 0;
return 0;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2006 by Texas Instruments
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*/
#ifndef __MUSB_OMAP243X_H__
#define __MUSB_OMAP243X_H__
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
#include <asm/arch/hardware.h>
#include <asm/arch/usb.h>
/*
* OMAP2430-specific definitions
*/
#define MENTOR_BASE_OFFSET 0
#if defined(CONFIG_ARCH_OMAP2430)
#define OMAP_HSOTG_BASE (OMAP243X_HS_BASE)
#elif defined(CONFIG_ARCH_OMAP3430)
#define OMAP_HSOTG_BASE (OMAP34XX_HSUSB_OTG_BASE)
#endif
#define OMAP_HSOTG(offset) (OMAP_HSOTG_BASE + 0x400 + (offset))
#define OTG_REVISION OMAP_HSOTG(0x0)
#define OTG_SYSCONFIG OMAP_HSOTG(0x4)
# define MIDLEMODE 12 /* bit position */
# define FORCESTDBY (0 << MIDLEMODE)
# define NOSTDBY (1 << MIDLEMODE)
# define SMARTSTDBY (2 << MIDLEMODE)
# define SIDLEMODE 3 /* bit position */
# define FORCEIDLE (0 << SIDLEMODE)
# define NOIDLE (1 << SIDLEMODE)
# define SMARTIDLE (2 << SIDLEMODE)
# define ENABLEWAKEUP (1 << 2)
# define SOFTRST (1 << 1)
# define AUTOIDLE (1 << 0)
#define OTG_SYSSTATUS OMAP_HSOTG(0x8)
# define RESETDONE (1 << 0)
#define OTG_INTERFSEL OMAP_HSOTG(0xc)
# define EXTCP (1 << 2)
# define PHYSEL 0 /* bit position */
# define UTMI_8BIT (0 << PHYSEL)
# define ULPI_12PIN (1 << PHYSEL)
# define ULPI_8PIN (2 << PHYSEL)
#define OTG_SIMENABLE OMAP_HSOTG(0x10)
# define TM1 (1 << 0)
#define OTG_FORCESTDBY OMAP_HSOTG(0x14)
# define ENABLEFORCE (1 << 0)
#endif /* CONFIG_ARCH_OMAP2430 */
#endif /* __MUSB_OMAP243X_H__ */

1151
drivers/usb/musb/tusb6010.c Normal file

File diff suppressed because it is too large Load Diff

402
drivers/usb/musb/tusb6010.h Normal file
View File

@ -0,0 +1,402 @@
/*
* Definitions for TUSB6010 USB 2.0 OTG Dual Role controller
*
* Copyright (C) 2006 Nokia Corporation
* Jarkko Nikula <jarkko.nikula@nokia.com>
* Tony Lindgren <tony@atomide.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __TUSB6010_H__
#define __TUSB6010_H__
extern u8 tusb_get_revision(struct musb *musb);
#ifdef CONFIG_USB_TUSB6010
#define musb_in_tusb() 1
#else
#define musb_in_tusb() 0
#endif
#ifdef CONFIG_USB_TUSB_OMAP_DMA
#define tusb_dma_omap() 1
#else
#define tusb_dma_omap() 0
#endif
/* VLYNQ control register. 32-bit at offset 0x000 */
#define TUSB_VLYNQ_CTRL 0x004
/* Mentor Graphics OTG core registers. 8,- 16- and 32-bit at offset 0x400 */
#define TUSB_BASE_OFFSET 0x400
/* FIFO registers 32-bit at offset 0x600 */
#define TUSB_FIFO_BASE 0x600
/* Device System & Control registers. 32-bit at offset 0x800 */
#define TUSB_SYS_REG_BASE 0x800
#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000)
#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16)
#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15)
#define TUSB_DEV_CONF_SOFT_ID (1 << 1)
#define TUSB_DEV_CONF_ID_SEL (1 << 0)
#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004)
#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008)
#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24)
#define TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP (1 << 23)
#define TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN (1 << 19)
#define TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN (1 << 18)
#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17)
#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16)
#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15)
#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14)
#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13)
#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12)
#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11)
#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10)
#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9)
#define TUSB_PHY_OTG_CTRL_PHYREF_CLKSEL(v) (((v) & 3) << 7)
#define TUSB_PHY_OTG_CTRL_PD (1 << 6)
#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5)
#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4)
#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3)
#define TUSB_PHY_OTG_CTRL_RESET (1 << 2)
#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1)
#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0)
/*OTG status register */
#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c)
#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8)
#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7)
#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6)
#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010)
# define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31)
# define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff)
#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014)
/* PRCM configuration register */
#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018)
#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24)
#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16)
/* PRCM management register */
#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c)
#define TUSB_PRCM_MNGMT_SRP_FIX_TIMER(v) (((v) & 0xf) << 25)
#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24)
#define TUSB_PRCM_MNGMT_VBUS_VALID_TIMER(v) (((v) & 0xf) << 20)
#define TUSB_PRCM_MNGMT_VBUS_VALID_FLT_EN (1 << 19)
#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18)
#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17)
#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10)
#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9)
#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8)
#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4)
#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3)
#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2)
#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1)
#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0)
/* Wake-up source clear and mask registers */
#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020)
#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028)
#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c)
#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13)
#define TUSB_PRCM_WGPIO_7 (1 << 12)
#define TUSB_PRCM_WGPIO_6 (1 << 11)
#define TUSB_PRCM_WGPIO_5 (1 << 10)
#define TUSB_PRCM_WGPIO_4 (1 << 9)
#define TUSB_PRCM_WGPIO_3 (1 << 8)
#define TUSB_PRCM_WGPIO_2 (1 << 7)
#define TUSB_PRCM_WGPIO_1 (1 << 6)
#define TUSB_PRCM_WGPIO_0 (1 << 5)
#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */
#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */
#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */
#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */
#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */
#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030)
#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034)
#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038)
#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c)
#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040)
#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044)
#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048)
#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c)
#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050)
#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054)
#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058)
#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c)
#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060)
#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064)
#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068)
#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c)
/* NOR flash interrupt source registers */
#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070)
#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074)
#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078)
#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c)
#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24)
#define TUSB_INT_SRC_USB_IP_CORE (1 << 17)
#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16)
#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15)
#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14)
#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13)
#define TUSB_INT_SRC_DEV_READY (1 << 12)
#define TUSB_INT_SRC_USB_IP_TX (1 << 9)
#define TUSB_INT_SRC_USB_IP_RX (1 << 8)
#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7)
#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6)
#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5)
#define TUSB_INT_SRC_USB_IP_CONN (1 << 4)
#define TUSB_INT_SRC_USB_IP_SOF (1 << 3)
#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2)
#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1)
#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0)
/* NOR flash interrupt registers reserved bits. Must be written as 0 */
#define TUSB_INT_MASK_RESERVED_17 (0x3fff << 17)
#define TUSB_INT_MASK_RESERVED_13 (1 << 13)
#define TUSB_INT_MASK_RESERVED_8 (0xf << 8)
#define TUSB_INT_SRC_RESERVED_26 (0x1f << 26)
#define TUSB_INT_SRC_RESERVED_18 (0x3f << 18)
#define TUSB_INT_SRC_RESERVED_10 (0x03 << 10)
/* Reserved bits for NOR flash interrupt mask and clear register */
#define TUSB_INT_MASK_RESERVED_BITS (TUSB_INT_MASK_RESERVED_17 | \
TUSB_INT_MASK_RESERVED_13 | \
TUSB_INT_MASK_RESERVED_8)
/* Reserved bits for NOR flash interrupt status register */
#define TUSB_INT_SRC_RESERVED_BITS (TUSB_INT_SRC_RESERVED_26 | \
TUSB_INT_SRC_RESERVED_18 | \
TUSB_INT_SRC_RESERVED_10)
#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080)
#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084)
#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100)
#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104)
#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108)
#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148)
/* Offsets from each ep base register */
#define TUSB_EP_TX_OFFSET 0x10c /* EP_IN in docs */
#define TUSB_EP_RX_OFFSET 0x14c /* EP_OUT in docs */
#define TUSB_EP_MAX_PACKET_SIZE_OFFSET 0x188
#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8)
#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4)
#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8)
/* Device System & Control register bitfields */
#define TUSB_INT_CTRL_CONF_INT_RELCYC(v) (((v) & 0x7) << 18)
#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17)
#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16)
#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24)
#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26)
#define TUSB_DMA_REQ_CONF_DMA_REQ_EN(v) (((v) & 0x3f) << 20)
#define TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(v) (((v) & 0xf) << 16)
#define TUSB_EP0_CONFIG_SW_EN (1 << 8)
#define TUSB_EP0_CONFIG_DIR_TX (1 << 7)
#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f)
#define TUSB_EP_CONFIG_SW_EN (1 << 31)
#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff)
#define TUSB_PROD_TEST_RESET_VAL 0xa596
#define TUSB_EP_FIFO(ep) (TUSB_FIFO_BASE + (ep) * 0x20)
#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8)
#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc)
#define TUSB_DIDR1_HI_CHIP_REV(v) (((v) >> 17) & 0xf)
#define TUSB_DIDR1_HI_REV_20 0
#define TUSB_DIDR1_HI_REV_30 1
#define TUSB_DIDR1_HI_REV_31 2
#define TUSB_REV_10 0x10
#define TUSB_REV_20 0x20
#define TUSB_REV_30 0x30
#define TUSB_REV_31 0x31
/*----------------------------------------------------------------------------*/
#ifdef CONFIG_USB_TUSB6010
/* configuration parameters specific to this silicon */
/* Number of Tx endpoints. Legal values are 1 - 16 (this value includes EP0) */
#define MUSB_C_NUM_EPT 16
/* Number of Rx endpoints. Legal values are 1 - 16 (this value includes EP0) */
#define MUSB_C_NUM_EPR 16
/* Endpoint 1 to 15 direction types. C_EP1_DEF is defined if either Tx endpoint
* 1 or Rx endpoint 1 are used.
*/
#define MUSB_C_EP1_DEF
/* C_EP1_TX_DEF is defined if Tx endpoint 1 is used */
#define MUSB_C_EP1_TX_DEF
/* C_EP1_RX_DEF is defined if Rx endpoint 1 is used */
#define MUSB_C_EP1_RX_DEF
/* C_EP1_TOR_DEF is defined if Tx endpoint 1 and Rx endpoint 1 share a FIFO */
/* #define C_EP1_TOR_DEF */
/* C_EP1_TAR_DEF is defined if both Tx endpoint 1 and Rx endpoint 1 are used
* and do not share a FIFO.
*/
#define MUSB_C_EP1_TAR_DEF
/* Similarly for all other used endpoints */
#define MUSB_C_EP2_DEF
#define MUSB_C_EP2_TX_DEF
#define MUSB_C_EP2_RX_DEF
#define MUSB_C_EP2_TAR_DEF
#define MUSB_C_EP3_DEF
#define MUSB_C_EP3_TX_DEF
#define MUSB_C_EP3_RX_DEF
#define MUSB_C_EP3_TAR_DEF
#define MUSB_C_EP4_DEF
#define MUSB_C_EP4_TX_DEF
#define MUSB_C_EP4_RX_DEF
#define MUSB_C_EP4_TAR_DEF
/* Endpoint 1 to 15 FIFO address bits. Legal values are 3 to 13 - corresponding
* to FIFO sizes of 8 to 8192 bytes. If an Tx endpoint shares a FIFO with an Rx
* endpoint then the Rx FIFO size must be the same as the Tx FIFO size. All
* endpoints 1 to 15 must be defined, unused endpoints should be set to 2.
*/
#define MUSB_C_EP1T_BITS 5
#define MUSB_C_EP1R_BITS 5
#define MUSB_C_EP2T_BITS 5
#define MUSB_C_EP2R_BITS 5
#define MUSB_C_EP3T_BITS 3
#define MUSB_C_EP3R_BITS 3
#define MUSB_C_EP4T_BITS 3
#define MUSB_C_EP4R_BITS 3
#define MUSB_C_EP5T_BITS 2
#define MUSB_C_EP5R_BITS 2
#define MUSB_C_EP6T_BITS 2
#define MUSB_C_EP6R_BITS 2
#define MUSB_C_EP7T_BITS 2
#define MUSB_C_EP7R_BITS 2
#define MUSB_C_EP8T_BITS 2
#define MUSB_C_EP8R_BITS 2
#define MUSB_C_EP9T_BITS 2
#define MUSB_C_EP9R_BITS 2
#define MUSB_C_EP10T_BITS 2
#define MUSB_C_EP10R_BITS 2
#define MUSB_C_EP11T_BITS 2
#define MUSB_C_EP11R_BITS 2
#define MUSB_C_EP12T_BITS 2
#define MUSB_C_EP12R_BITS 2
#define MUSB_C_EP13T_BITS 2
#define MUSB_C_EP13R_BITS 2
#define MUSB_C_EP14T_BITS 2
#define MUSB_C_EP14R_BITS 2
#define MUSB_C_EP15T_BITS 2
#define MUSB_C_EP15R_BITS 2
/* Define the following constant if the USB2.0 Transceiver Macrocell data width
* is 16-bits.
*/
/* #define C_UTM_16 */
/* Define this constant if the CPU uses big-endian byte ordering. */
/* #define C_BIGEND */
/* Define the following constant if any Tx endpoint is required to support
* multiple bulk packets.
*/
/* #define C_MP_TX */
/* Define the following constant if any Rx endpoint is required to support
* multiple bulk packets.
*/
/* #define C_MP_RX */
/* Define the following constant if any Tx endpoint is required to support high
* bandwidth ISO.
*/
/* #define C_HB_TX */
/* Define the following constant if any Rx endpoint is required to support high
* bandwidth ISO.
*/
/* #define C_HB_RX */
/* Define the following constant if software connect/disconnect control is
* required.
*/
#define MUSB_C_SOFT_CON
/* Define the following constant if Vendor Control Registers are required. */
/* #define C_VEND_REG */
/* Vendor control register widths. */
#define MUSB_C_VCTL_BITS 4
#define MUSB_C_VSTAT_BITS 8
/* Define the following constant to include a DMA controller. */
/* #define C_DMA */
/* Define the following constant if 2 or more DMA channels are required. */
/* #define C_DMA2 */
/* Define the following constant if 3 or more DMA channels are required. */
/* #define C_DMA3 */
/* Define the following constant if 4 or more DMA channels are required. */
/* #define C_DMA4 */
/* Define the following constant if 5 or more DMA channels are required. */
/* #define C_DMA5 */
/* Define the following constant if 6 or more DMA channels are required. */
/* #define C_DMA6 */
/* Define the following constant if 7 or more DMA channels are required. */
/* #define C_DMA7 */
/* Define the following constant if 8 or more DMA channels are required. */
/* #define C_DMA8 */
/* Enable Dynamic FIFO Sizing */
#define MUSB_C_DYNFIFO_DEF
/* Derived constants. The following constants are derived from the previous
* configuration constants
*/
/* Total number of endpoints. Legal values are 2 - 16. This must be equal to
* the larger of C_NUM_EPT, C_NUM_EPR
*/
/* #define MUSB_C_NUM_EPS 5 */
/* C_EPMAX_BITS is equal to the largest endpoint FIFO word address bits */
#define MUSB_C_EPMAX_BITS 11
/* C_RAM_BITS is the number of address bits required to address the RAM (32-bit
* addresses). It is defined as log2 of the sum of 2** of all the endpoint FIFO
* dword address bits (rounded up).
*/
#define MUSB_C_RAM_BITS 12
#endif /* CONFIG_USB_TUSB6010 */
#endif /* __TUSB6010_H__ */

View File

@ -0,0 +1,719 @@
/*
* TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface
*
* Copyright (C) 2006 Nokia Corporation
* Tony Lindgren <tony@atomide.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <asm/arch/dma.h>
#include <asm/arch/mux.h>
#include "musb_core.h"
#define to_chdat(c) ((struct tusb_omap_dma_ch *)(c)->private_data)
#define MAX_DMAREQ 5 /* REVISIT: Really 6, but req5 not OK */
struct tusb_omap_dma_ch {
struct musb *musb;
void __iomem *tbase;
unsigned long phys_offset;
int epnum;
u8 tx;
struct musb_hw_ep *hw_ep;
int ch;
s8 dmareq;
s8 sync_dev;
struct tusb_omap_dma *tusb_dma;
void __iomem *dma_addr;
u32 len;
u16 packet_sz;
u16 transfer_packet_sz;
u32 transfer_len;
u32 completed_len;
};
struct tusb_omap_dma {
struct dma_controller controller;
struct musb *musb;
void __iomem *tbase;
int ch;
s8 dmareq;
s8 sync_dev;
unsigned multichannel:1;
};
static int tusb_omap_dma_start(struct dma_controller *c)
{
struct tusb_omap_dma *tusb_dma;
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
/* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
return 0;
}
static int tusb_omap_dma_stop(struct dma_controller *c)
{
struct tusb_omap_dma *tusb_dma;
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
/* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */
return 0;
}
/*
* Allocate dmareq0 to the current channel unless it's already taken
*/
static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat)
{
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
if (reg != 0) {
DBG(3, "ep%i dmareq0 is busy for ep%i\n",
chdat->epnum, reg & 0xf);
return -EAGAIN;
}
if (chdat->tx)
reg = (1 << 4) | chdat->epnum;
else
reg = chdat->epnum;
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
return 0;
}
static inline void tusb_omap_free_shared_dmareq(struct tusb_omap_dma_ch *chdat)
{
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
if ((reg & 0xf) != chdat->epnum) {
printk(KERN_ERR "ep%i trying to release dmareq0 for ep%i\n",
chdat->epnum, reg & 0xf);
return;
}
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, 0);
}
/*
* See also musb_dma_completion in plat_uds.c and musb_g_[tx|rx]() in
* musb_gadget.c.
*/
static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data)
{
struct dma_channel *channel = (struct dma_channel *)data;
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
struct musb *musb = chdat->musb;
struct musb_hw_ep *hw_ep = chdat->hw_ep;
void __iomem *ep_conf = hw_ep->conf;
void __iomem *mbase = musb->mregs;
unsigned long remaining, flags, pio;
int ch;
spin_lock_irqsave(&musb->lock, flags);
if (tusb_dma->multichannel)
ch = chdat->ch;
else
ch = tusb_dma->ch;
if (ch_status != OMAP_DMA_BLOCK_IRQ)
printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status);
DBG(3, "ep%i %s dma callback ch: %i status: %x\n",
chdat->epnum, chdat->tx ? "tx" : "rx",
ch, ch_status);
if (chdat->tx)
remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
else
remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining);
/* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */
if (unlikely(remaining > chdat->transfer_len)) {
DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n",
chdat->tx ? "tx" : "rx", chdat->ch,
remaining);
remaining = 0;
}
channel->actual_len = chdat->transfer_len - remaining;
pio = chdat->len - channel->actual_len;
DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len);
/* Transfer remaining 1 - 31 bytes */
if (pio > 0 && pio < 32) {
u8 *buf;
DBG(3, "Using PIO for remaining %lu bytes\n", pio);
buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len;
if (chdat->tx) {
dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
chdat->transfer_len, DMA_TO_DEVICE);
musb_write_fifo(hw_ep, pio, buf);
} else {
musb_read_fifo(hw_ep, pio, buf);
dma_cache_maint(phys_to_virt((u32)chdat->dma_addr),
chdat->transfer_len, DMA_FROM_DEVICE);
}
channel->actual_len += pio;
}
if (!tusb_dma->multichannel)
tusb_omap_free_shared_dmareq(chdat);
channel->status = MUSB_DMA_STATUS_FREE;
/* Handle only RX callbacks here. TX callbacks must be handled based
* on the TUSB DMA status interrupt.
* REVISIT: Use both TUSB DMA status interrupt and OMAP DMA callback
* interrupt for RX and TX.
*/
if (!chdat->tx)
musb_dma_completion(musb, chdat->epnum, chdat->tx);
/* We must terminate short tx transfers manually by setting TXPKTRDY.
* REVISIT: This same problem may occur with other MUSB dma as well.
* Easy to test with g_ether by pinging the MUSB board with ping -s54.
*/
if ((chdat->transfer_len < chdat->packet_sz)
|| (chdat->transfer_len % chdat->packet_sz != 0)) {
u16 csr;
if (chdat->tx) {
DBG(3, "terminating short tx packet\n");
musb_ep_select(mbase, chdat->epnum);
csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY
| MUSB_TXCSR_P_WZC_BITS;
musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
}
}
spin_unlock_irqrestore(&musb->lock, flags);
}
static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
u8 rndis_mode, dma_addr_t dma_addr, u32 len)
{
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
struct musb *musb = chdat->musb;
struct musb_hw_ep *hw_ep = chdat->hw_ep;
void __iomem *mbase = musb->mregs;
void __iomem *ep_conf = hw_ep->conf;
dma_addr_t fifo = hw_ep->fifo_sync;
struct omap_dma_channel_params dma_params;
u32 dma_remaining;
int src_burst, dst_burst;
u16 csr;
int ch;
s8 dmareq;
s8 sync_dev;
if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz))
return false;
/*
* HW issue #10: Async dma will eventually corrupt the XFR_SIZE
* register which will cause missed DMA interrupt. We could try to
* use a timer for the callback, but it is unsafe as the XFR_SIZE
* register is corrupt, and we won't know if the DMA worked.
*/
if (dma_addr & 0x2)
return false;
/*
* Because of HW issue #10, it seems like mixing sync DMA and async
* PIO access can confuse the DMA. Make sure XFR_SIZE is reset before
* using the channel for DMA.
*/
if (chdat->tx)
dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET);
else
dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET);
dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining);
if (dma_remaining) {
DBG(2, "Busy %s dma ch%i, not using: %08x\n",
chdat->tx ? "tx" : "rx", chdat->ch,
dma_remaining);
return false;
}
chdat->transfer_len = len & ~0x1f;
if (len < packet_sz)
chdat->transfer_packet_sz = chdat->transfer_len;
else
chdat->transfer_packet_sz = packet_sz;
if (tusb_dma->multichannel) {
ch = chdat->ch;
dmareq = chdat->dmareq;
sync_dev = chdat->sync_dev;
} else {
if (tusb_omap_use_shared_dmareq(chdat) != 0) {
DBG(3, "could not get dma for ep%i\n", chdat->epnum);
return false;
}
if (tusb_dma->ch < 0) {
/* REVISIT: This should get blocked earlier, happens
* with MSC ErrorRecoveryTest
*/
WARN_ON(1);
return false;
}
ch = tusb_dma->ch;
dmareq = tusb_dma->dmareq;
sync_dev = tusb_dma->sync_dev;
omap_set_dma_callback(ch, tusb_omap_dma_cb, channel);
}
chdat->packet_sz = packet_sz;
chdat->len = len;
channel->actual_len = 0;
chdat->dma_addr = (void __iomem *)dma_addr;
channel->status = MUSB_DMA_STATUS_BUSY;
/* Since we're recycling dma areas, we need to clean or invalidate */
if (chdat->tx)
dma_cache_maint(phys_to_virt(dma_addr), len, DMA_TO_DEVICE);
else
dma_cache_maint(phys_to_virt(dma_addr), len, DMA_FROM_DEVICE);
/* Use 16-bit transfer if dma_addr is not 32-bit aligned */
if ((dma_addr & 0x3) == 0) {
dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
dma_params.elem_count = 8; /* Elements in frame */
} else {
dma_params.data_type = OMAP_DMA_DATA_TYPE_S16;
dma_params.elem_count = 16; /* Elements in frame */
fifo = hw_ep->fifo_async;
}
dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */
DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n",
chdat->epnum, chdat->tx ? "tx" : "rx",
ch, dma_addr, chdat->transfer_len, len,
chdat->transfer_packet_sz, packet_sz);
/*
* Prepare omap DMA for transfer
*/
if (chdat->tx) {
dma_params.src_amode = OMAP_DMA_AMODE_POST_INC;
dma_params.src_start = (unsigned long)dma_addr;
dma_params.src_ei = 0;
dma_params.src_fi = 0;
dma_params.dst_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
dma_params.dst_start = (unsigned long)fifo;
dma_params.dst_ei = 1;
dma_params.dst_fi = -31; /* Loop 32 byte window */
dma_params.trigger = sync_dev;
dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
dma_params.src_or_dst_synch = 0; /* Dest sync */
src_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 read */
dst_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 write */
} else {
dma_params.src_amode = OMAP_DMA_AMODE_DOUBLE_IDX;
dma_params.src_start = (unsigned long)fifo;
dma_params.src_ei = 1;
dma_params.src_fi = -31; /* Loop 32 byte window */
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
dma_params.dst_start = (unsigned long)dma_addr;
dma_params.dst_ei = 0;
dma_params.dst_fi = 0;
dma_params.trigger = sync_dev;
dma_params.sync_mode = OMAP_DMA_SYNC_FRAME;
dma_params.src_or_dst_synch = 1; /* Source sync */
src_burst = OMAP_DMA_DATA_BURST_8; /* 8x32 read */
dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */
}
DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n",
chdat->epnum, chdat->tx ? "tx" : "rx",
(dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16,
((dma_addr & 0x3) == 0) ? "sync" : "async",
dma_params.src_start, dma_params.dst_start);
omap_set_dma_params(ch, &dma_params);
omap_set_dma_src_burst_mode(ch, src_burst);
omap_set_dma_dest_burst_mode(ch, dst_burst);
omap_set_dma_write_mode(ch, OMAP_DMA_WRITE_LAST_NON_POSTED);
/*
* Prepare MUSB for DMA transfer
*/
if (chdat->tx) {
musb_ep_select(mbase, chdat->epnum);
csr = musb_readw(hw_ep->regs, MUSB_TXCSR);
csr |= (MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB
| MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE);
csr &= ~MUSB_TXCSR_P_UNDERRUN;
musb_writew(hw_ep->regs, MUSB_TXCSR, csr);
} else {
musb_ep_select(mbase, chdat->epnum);
csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
csr |= MUSB_RXCSR_DMAENAB;
csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE);
musb_writew(hw_ep->regs, MUSB_RXCSR,
csr | MUSB_RXCSR_P_WZC_BITS);
}
/*
* Start DMA transfer
*/
omap_start_dma(ch);
if (chdat->tx) {
/* Send transfer_packet_sz packets at a time */
musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
chdat->transfer_packet_sz);
musb_writel(ep_conf, TUSB_EP_TX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
} else {
/* Receive transfer_packet_sz packets at a time */
musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET,
chdat->transfer_packet_sz << 16);
musb_writel(ep_conf, TUSB_EP_RX_OFFSET,
TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len));
}
return true;
}
static int tusb_omap_dma_abort(struct dma_channel *channel)
{
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
struct tusb_omap_dma *tusb_dma = chdat->tusb_dma;
if (!tusb_dma->multichannel) {
if (tusb_dma->ch >= 0) {
omap_stop_dma(tusb_dma->ch);
omap_free_dma(tusb_dma->ch);
tusb_dma->ch = -1;
}
tusb_dma->dmareq = -1;
tusb_dma->sync_dev = -1;
}
channel->status = MUSB_DMA_STATUS_FREE;
return 0;
}
static inline int tusb_omap_dma_allocate_dmareq(struct tusb_omap_dma_ch *chdat)
{
u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
int i, dmareq_nr = -1;
const int sync_dev[6] = {
OMAP24XX_DMA_EXT_DMAREQ0,
OMAP24XX_DMA_EXT_DMAREQ1,
OMAP242X_DMA_EXT_DMAREQ2,
OMAP242X_DMA_EXT_DMAREQ3,
OMAP242X_DMA_EXT_DMAREQ4,
OMAP242X_DMA_EXT_DMAREQ5,
};
for (i = 0; i < MAX_DMAREQ; i++) {
int cur = (reg & (0xf << (i * 5))) >> (i * 5);
if (cur == 0) {
dmareq_nr = i;
break;
}
}
if (dmareq_nr == -1)
return -EAGAIN;
reg |= (chdat->epnum << (dmareq_nr * 5));
if (chdat->tx)
reg |= ((1 << 4) << (dmareq_nr * 5));
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
chdat->dmareq = dmareq_nr;
chdat->sync_dev = sync_dev[chdat->dmareq];
return 0;
}
static inline void tusb_omap_dma_free_dmareq(struct tusb_omap_dma_ch *chdat)
{
u32 reg;
if (!chdat || chdat->dmareq < 0)
return;
reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP);
reg &= ~(0x1f << (chdat->dmareq * 5));
musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg);
chdat->dmareq = -1;
chdat->sync_dev = -1;
}
static struct dma_channel *dma_channel_pool[MAX_DMAREQ];
static struct dma_channel *
tusb_omap_dma_allocate(struct dma_controller *c,
struct musb_hw_ep *hw_ep,
u8 tx)
{
int ret, i;
const char *dev_name;
struct tusb_omap_dma *tusb_dma;
struct musb *musb;
void __iomem *tbase;
struct dma_channel *channel = NULL;
struct tusb_omap_dma_ch *chdat = NULL;
u32 reg;
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
musb = tusb_dma->musb;
tbase = musb->ctrl_base;
reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
if (tx)
reg &= ~(1 << hw_ep->epnum);
else
reg &= ~(1 << (hw_ep->epnum + 15));
musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
/* REVISIT: Why does dmareq5 not work? */
if (hw_ep->epnum == 0) {
DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx");
return NULL;
}
for (i = 0; i < MAX_DMAREQ; i++) {
struct dma_channel *ch = dma_channel_pool[i];
if (ch->status == MUSB_DMA_STATUS_UNKNOWN) {
ch->status = MUSB_DMA_STATUS_FREE;
channel = ch;
chdat = ch->private_data;
break;
}
}
if (!channel)
return NULL;
if (tx) {
chdat->tx = 1;
dev_name = "TUSB transmit";
} else {
chdat->tx = 0;
dev_name = "TUSB receive";
}
chdat->musb = tusb_dma->musb;
chdat->tbase = tusb_dma->tbase;
chdat->hw_ep = hw_ep;
chdat->epnum = hw_ep->epnum;
chdat->dmareq = -1;
chdat->completed_len = 0;
chdat->tusb_dma = tusb_dma;
channel->max_len = 0x7fffffff;
channel->desired_mode = 0;
channel->actual_len = 0;
if (tusb_dma->multichannel) {
ret = tusb_omap_dma_allocate_dmareq(chdat);
if (ret != 0)
goto free_dmareq;
ret = omap_request_dma(chdat->sync_dev, dev_name,
tusb_omap_dma_cb, channel, &chdat->ch);
if (ret != 0)
goto free_dmareq;
} else if (tusb_dma->ch == -1) {
tusb_dma->dmareq = 0;
tusb_dma->sync_dev = OMAP24XX_DMA_EXT_DMAREQ0;
/* Callback data gets set later in the shared dmareq case */
ret = omap_request_dma(tusb_dma->sync_dev, "TUSB shared",
tusb_omap_dma_cb, NULL, &tusb_dma->ch);
if (ret != 0)
goto free_dmareq;
chdat->dmareq = -1;
chdat->ch = -1;
}
DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n",
chdat->epnum,
chdat->tx ? "tx" : "rx",
chdat->ch >= 0 ? "dedicated" : "shared",
chdat->ch >= 0 ? chdat->ch : tusb_dma->ch,
chdat->dmareq >= 0 ? chdat->dmareq : tusb_dma->dmareq,
chdat->sync_dev >= 0 ? chdat->sync_dev : tusb_dma->sync_dev);
return channel;
free_dmareq:
tusb_omap_dma_free_dmareq(chdat);
DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum);
channel->status = MUSB_DMA_STATUS_UNKNOWN;
return NULL;
}
static void tusb_omap_dma_release(struct dma_channel *channel)
{
struct tusb_omap_dma_ch *chdat = to_chdat(channel);
struct musb *musb = chdat->musb;
void __iomem *tbase = musb->ctrl_base;
u32 reg;
DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch);
reg = musb_readl(tbase, TUSB_DMA_INT_MASK);
if (chdat->tx)
reg |= (1 << chdat->epnum);
else
reg |= (1 << (chdat->epnum + 15));
musb_writel(tbase, TUSB_DMA_INT_MASK, reg);
reg = musb_readl(tbase, TUSB_DMA_INT_CLEAR);
if (chdat->tx)
reg |= (1 << chdat->epnum);
else
reg |= (1 << (chdat->epnum + 15));
musb_writel(tbase, TUSB_DMA_INT_CLEAR, reg);
channel->status = MUSB_DMA_STATUS_UNKNOWN;
if (chdat->ch >= 0) {
omap_stop_dma(chdat->ch);
omap_free_dma(chdat->ch);
chdat->ch = -1;
}
if (chdat->dmareq >= 0)
tusb_omap_dma_free_dmareq(chdat);
channel = NULL;
}
void dma_controller_destroy(struct dma_controller *c)
{
struct tusb_omap_dma *tusb_dma;
int i;
tusb_dma = container_of(c, struct tusb_omap_dma, controller);
for (i = 0; i < MAX_DMAREQ; i++) {
struct dma_channel *ch = dma_channel_pool[i];
if (ch) {
kfree(ch->private_data);
kfree(ch);
}
}
if (!tusb_dma->multichannel && tusb_dma && tusb_dma->ch >= 0)
omap_free_dma(tusb_dma->ch);
kfree(tusb_dma);
}
struct dma_controller *__init
dma_controller_create(struct musb *musb, void __iomem *base)
{
void __iomem *tbase = musb->ctrl_base;
struct tusb_omap_dma *tusb_dma;
int i;
/* REVISIT: Get dmareq lines used from board-*.c */
musb_writel(musb->ctrl_base, TUSB_DMA_INT_MASK, 0x7fffffff);
musb_writel(musb->ctrl_base, TUSB_DMA_EP_MAP, 0);
musb_writel(tbase, TUSB_DMA_REQ_CONF,
TUSB_DMA_REQ_CONF_BURST_SIZE(2)
| TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f)
| TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2));
tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL);
if (!tusb_dma)
goto cleanup;
tusb_dma->musb = musb;
tusb_dma->tbase = musb->ctrl_base;
tusb_dma->ch = -1;
tusb_dma->dmareq = -1;
tusb_dma->sync_dev = -1;
tusb_dma->controller.start = tusb_omap_dma_start;
tusb_dma->controller.stop = tusb_omap_dma_stop;
tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate;
tusb_dma->controller.channel_release = tusb_omap_dma_release;
tusb_dma->controller.channel_program = tusb_omap_dma_program;
tusb_dma->controller.channel_abort = tusb_omap_dma_abort;
if (tusb_get_revision(musb) >= TUSB_REV_30)
tusb_dma->multichannel = 1;
for (i = 0; i < MAX_DMAREQ; i++) {
struct dma_channel *ch;
struct tusb_omap_dma_ch *chdat;
ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL);
if (!ch)
goto cleanup;
dma_channel_pool[i] = ch;
chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL);
if (!chdat)
goto cleanup;
ch->status = MUSB_DMA_STATUS_UNKNOWN;
ch->private_data = chdat;
}
return &tusb_dma->controller;
cleanup:
dma_controller_destroy(&tusb_dma->controller);
return NULL;
}

70
include/linux/usb/musb.h Normal file
View File

@ -0,0 +1,70 @@
/*
* This is used to for host and peripheral modes of the driver for
* Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC.
*
* Board initialization should put one of these into dev->platform_data,
* probably on some platform_device named "musb_hdrc". It encapsulates
* key configuration differences between boards.
*/
/* The USB role is defined by the connector used on the board, so long as
* standards are being followed. (Developer boards sometimes won't.)
*/
enum musb_mode {
MUSB_UNDEFINED = 0,
MUSB_HOST, /* A or Mini-A connector */
MUSB_PERIPHERAL, /* B or Mini-B connector */
MUSB_OTG /* Mini-AB connector */
};
struct clk;
struct musb_hdrc_platform_data {
/* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
u8 mode;
/* for clk_get() */
const char *clock;
/* (HOST or OTG) switch VBUS on/off */
int (*set_vbus)(struct device *dev, int is_on);
/* (HOST or OTG) mA/2 power supplied on (default = 8mA) */
u8 power;
/* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */
u8 min_power;
/* (HOST or OTG) msec/2 after VBUS on till power good */
u8 potpgt;
/* TBD: chip defaults should probably go someplace else,
* e.g. number of tx/rx endpoints, etc
*/
unsigned multipoint:1;
/* Power the device on or off */
int (*set_power)(int state);
/* Turn device clock on or off */
int (*set_clock)(struct clk *clock, int is_on);
};
/* TUSB 6010 support */
#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */
#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */
#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */
#ifdef CONFIG_ARCH_OMAP2
extern int __init tusb6010_setup_interface(
struct musb_hdrc_platform_data *data,
unsigned ps_refclk, unsigned waitpin,
unsigned async_cs, unsigned sync_cs,
unsigned irq, unsigned dmachan);
extern int tusb6010_platform_retime(unsigned is_refclk);
#endif /* OMAP2 */