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:
Родитель
f331e40ee8
Коммит
550a7375fe
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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__ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 */
|
|
@ -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,
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 */
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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__ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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__ */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Загрузка…
Ссылка в новой задаче