1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-06-30 23:56:24 +03:00

cns3xxx: convert dwc_otg patches to files

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34162 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
luka 2012-11-11 20:03:58 +00:00
parent 133d66da06
commit 00f29ce9a4
20 changed files with 22735 additions and 22807 deletions

View File

@ -0,0 +1,44 @@
#
# USB Dual Role (OTG-ready) Controller Drivers
# for silicon based on Synopsys DesignWare IP
#
comment "Enable Host or Gadget support for DesignWare OTG controller"
depends on !USB && USB_GADGET=n
config USB_DWC_OTG
tristate "Synopsys DWC OTG Controller"
depends on USB
help
This driver provides USB Device Controller support for the
Synopsys DesignWare USB OTG Core used on the Cavium CNS34xx SOC.
config DWC_DEBUG
bool "Enable DWC Debugging"
depends on USB_DWC_OTG
default n
help
Enable DWC driver debugging
choice
prompt "DWC Mode Selection"
depends on USB_DWC_OTG
default DWC_HOST_ONLY
help
Select the DWC Core in OTG, Host only, or Device only mode.
config DWC_HOST_ONLY
bool "DWC Host Only Mode"
config DWC_OTG_MODE
bool "DWC OTG Mode"
select USB_GADGET
select USB_GADGET_SELECTED
config DWC_DEVICE_ONLY
bool "DWC Device Only Mode"
select USB_GADGET
select USB_GADGET_SELECTED
endchoice

View File

@ -0,0 +1,26 @@
#
# Makefile for DWC_otg Highspeed USB controller driver
#
EXTRA_CFLAGS += -DDWC_HS_ELECT_TST
#EXTRA_CFLAGS += -Dlinux -DDWC_HS_ELECT_TST
#EXTRA_CFLAGS += -DDWC_EN_ISOC
ifneq ($(CONFIG_DWC_HOST_ONLY),)
EXTRA_CFLAGS += -DDWC_HOST_ONLY
endif
ifneq ($(CONFIG_DWC_DEVICE_ONLY),)
EXTRA_CFLAGS += -DDWC_DEVICE_ONLY
endif
ifneq ($(CONFIG_DWC_DEBUG),)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_USB_DWC_OTG) := dwc_otg.o
dwc_otg-objs := otg_driver.o otg_attr.o
dwc_otg-objs += otg_cil.o otg_cil_intr.o
dwc_otg-objs += otg_pcd.o otg_pcd_intr.o
dwc_otg-objs += otg_hcd.o otg_hcd_intr.o otg_hcd_queue.o

View File

@ -0,0 +1,886 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $
* $Revision: #31 $
* $Date: 2008/07/15 $
* $Change: 1064918 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
* ========================================================================== */
/** @file
*
* The diagnostic interface will provide access to the controller for
* bringing up the hardware and testing. The Linux driver attributes
* feature will be used to provide the Linux Diagnostic
* Interface. These attributes are accessed through sysfs.
*/
/** @page "Linux Module Attributes"
*
* The Linux module attributes feature is used to provide the Linux
* Diagnostic Interface. These attributes are accessed through sysfs.
* The diagnostic interface will provide access to the controller for
* bringing up the hardware and testing.
The following table shows the attributes.
<table>
<tr>
<td><b> Name</b></td>
<td><b> Description</b></td>
<td><b> Access</b></td>
</tr>
<tr>
<td> mode </td>
<td> Returns the current mode: 0 for device mode, 1 for host mode</td>
<td> Read</td>
</tr>
<tr>
<td> hnpcapable </td>
<td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register.
Read returns the current value.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> srpcapable </td>
<td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register.
Read returns the current value.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> hnp </td>
<td> Initiates the Host Negotiation Protocol. Read returns the status.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> srp </td>
<td> Initiates the Session Request Protocol. Read returns the status.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> buspower </td>
<td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td>
<td> Read/Write</td>
</tr>
<tr>
<td> bussuspend </td>
<td> Suspends the USB bus.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> busconnected </td>
<td> Gets the connection status of the bus</td>
<td> Read</td>
</tr>
<tr>
<td> gotgctl </td>
<td> Gets or sets the Core Control Status Register.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> gusbcfg </td>
<td> Gets or sets the Core USB Configuration Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> grxfsiz </td>
<td> Gets or sets the Receive FIFO Size Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> gnptxfsiz </td>
<td> Gets or sets the non-periodic Transmit Size Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> gpvndctl </td>
<td> Gets or sets the PHY Vendor Control Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> ggpio </td>
<td> Gets the value in the lower 16-bits of the General Purpose IO Register
or sets the upper 16 bits.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> guid </td>
<td> Gets or sets the value of the User ID Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> gsnpsid </td>
<td> Gets the value of the Synopsys ID Regester</td>
<td> Read</td>
</tr>
<tr>
<td> devspeed </td>
<td> Gets or sets the device speed setting in the DCFG register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> enumspeed </td>
<td> Gets the device enumeration Speed.</td>
<td> Read</td>
</tr>
<tr>
<td> hptxfsiz </td>
<td> Gets the value of the Host Periodic Transmit FIFO</td>
<td> Read</td>
</tr>
<tr>
<td> hprt0 </td>
<td> Gets or sets the value in the Host Port Control and Status Register</td>
<td> Read/Write</td>
</tr>
<tr>
<td> regoffset </td>
<td> Sets the register offset for the next Register Access</td>
<td> Read/Write</td>
</tr>
<tr>
<td> regvalue </td>
<td> Gets or sets the value of the register at the offset in the regoffset attribute.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> remote_wakeup </td>
<td> On read, shows the status of Remote Wakeup. On write, initiates a remote
wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote
Wakeup signalling bit in the Device Control Register is set for 1
milli-second.</td>
<td> Read/Write</td>
</tr>
<tr>
<td> regdump </td>
<td> Dumps the contents of core registers.</td>
<td> Read</td>
</tr>
<tr>
<td> spramdump </td>
<td> Dumps the contents of core registers.</td>
<td> Read</td>
</tr>
<tr>
<td> hcddump </td>
<td> Dumps the current HCD state.</td>
<td> Read</td>
</tr>
<tr>
<td> hcd_frrem </td>
<td> Shows the average value of the Frame Remaining
field in the Host Frame Number/Frame Remaining register when an SOF interrupt
occurs. This can be used to determine the average interrupt latency. Also
shows the average Frame Remaining value for start_transfer and the "a" and
"b" sample points. The "a" and "b" sample points may be used during debugging
bto determine how long it takes to execute a section of the HCD code.</td>
<td> Read</td>
</tr>
<tr>
<td> rd_reg_test </td>
<td> Displays the time required to read the GNPTXFSIZ register many times
(the output shows the number of times the register is read).
<td> Read</td>
</tr>
<tr>
<td> wr_reg_test </td>
<td> Displays the time required to write the GNPTXFSIZ register many times
(the output shows the number of times the register is written).
<td> Read</td>
</tr>
</table>
Example usage:
To get the current mode:
cat /sys/devices/lm0/mode
To power down the USB:
echo 0 > /sys/devices/lm0/buspower
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/stat.h> /* permission constants */
#include <linux/version.h>
#include <asm/sizes.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include "otg_plat.h"
#include "otg_attr.h"
#include "otg_driver.h"
#include "otg_pcd.h"
#include "otg_hcd.h"
/*
* MACROs for defining sysfs attribute
*/
#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
{ \
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t val; \
val = dwc_read_reg32 (_addr_); \
val = (val & (_mask_)) >> _shift_; \
return sprintf (buf, "%s = 0x%x\n", _string_, val); \
}
#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t set = simple_strtoul(buf, NULL, 16); \
uint32_t clear = set; \
clear = ((~clear) << _shift_) & _mask_; \
set = (set << _shift_) & _mask_; \
dev_dbg(_dev, "Storing Address=0x%08x Set=0x%08x Clear=0x%08x\n", (uint32_t)_addr_, set, clear); \
dwc_modify_reg32(_addr_, clear, set); \
return count; \
}
/*
* MACROs for defining sysfs attribute for 32-bit registers
*/
#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \
static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \
{ \
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t val; \
val = dwc_read_reg32 (_addr_); \
return sprintf (buf, "%s = 0x%08x\n", _string_, val); \
}
#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \
static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t val = simple_strtoul(buf, NULL, 16); \
dev_dbg(_dev, "Storing Address=0x%08x Val=0x%08x\n", (uint32_t)_addr_, val); \
dwc_write_reg32(_addr_, val); \
return count; \
}
#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \
DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \
DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \
DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \
DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store);
#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \
DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \
DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL);
/** @name Functions for Show/Store of Attributes */
/**@{*/
/**
* Show the register offset of the Register Access.
*/
static ssize_t regoffset_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
return snprintf(buf, sizeof("0xFFFFFFFF\n")+1,"0x%08x\n", otg_dev->reg_offset);
}
/**
* Set the register offset for the next Register Access Read/Write
*/
static ssize_t regoffset_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t offset = simple_strtoul(buf, NULL, 16);
//dev_dbg(_dev, "Offset=0x%08x\n", offset);
if (offset < SZ_256K ) {
otg_dev->reg_offset = offset;
}
else {
dev_err( _dev, "invalid offset\n" );
}
return count;
}
DEVICE_ATTR(regoffset, S_IRUGO|S_IWUSR, (void *)regoffset_show, regoffset_store);
/**
* Show the value of the register at the offset in the reg_offset
* attribute.
*/
static ssize_t regvalue_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t val;
volatile uint32_t *addr;
if (otg_dev->reg_offset != 0xFFFFFFFF &&
0 != otg_dev->base) {
/* Calculate the address */
addr = (uint32_t*)(otg_dev->reg_offset +
(uint8_t*)otg_dev->base);
//dev_dbg(_dev, "@0x%08x\n", (unsigned)addr);
val = dwc_read_reg32( addr );
return snprintf(buf, sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n")+1,
"Reg@0x%06x = 0x%08x\n",
otg_dev->reg_offset, val);
}
else {
dev_err(_dev, "Invalid offset (0x%0x)\n",
otg_dev->reg_offset);
return sprintf(buf, "invalid offset\n" );
}
}
/**
* Store the value in the register at the offset in the reg_offset
* attribute.
*
*/
static ssize_t regvalue_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
volatile uint32_t * addr;
uint32_t val = simple_strtoul(buf, NULL, 16);
//dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val);
if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) {
/* Calculate the address */
addr = (uint32_t*)(otg_dev->reg_offset +
(uint8_t*)otg_dev->base);
//dev_dbg(_dev, "@0x%08x\n", (unsigned)addr);
dwc_write_reg32( addr, val );
}
else {
dev_err(_dev, "Invalid Register Offset (0x%08x)\n",
otg_dev->reg_offset);
}
return count;
}
DEVICE_ATTR(regvalue, S_IRUGO|S_IWUSR, regvalue_show, regvalue_store);
/*
* Attributes
*/
DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<20),20,"Mode");
DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<9),9,"Mode");
DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<8),8,"Mode");
//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode");
DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected,otg_dev->core_if->host_if->hprt0,0x01,0,"Bus Connected");
DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl,&(otg_dev->core_if->core_global_regs->gotgctl),"GOTGCTL");
DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,&(otg_dev->core_if->core_global_regs->gusbcfg),"GUSBCFG");
DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,&(otg_dev->core_if->core_global_regs->grxfsiz),"GRXFSIZ");
DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,&(otg_dev->core_if->core_global_regs->gnptxfsiz),"GNPTXFSIZ");
DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,&(otg_dev->core_if->core_global_regs->gpvndctl),"GPVNDCTL");
DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,&(otg_dev->core_if->core_global_regs->ggpio),"GGPIO");
DWC_OTG_DEVICE_ATTR_REG32_RW(guid,&(otg_dev->core_if->core_global_regs->guid),"GUID");
DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,&(otg_dev->core_if->core_global_regs->gsnpsid),"GSNPSID");
DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dcfg),0x3,0,"Device Speed");
DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dsts),0x6,1,"Device Enumeration Speed");
DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,&(otg_dev->core_if->core_global_regs->hptxfsiz),"HPTXFSIZ");
DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0,otg_dev->core_if->host_if->hprt0,"HPRT0");
/**
* @todo Add code to initiate the HNP.
*/
/**
* Show the HNP status bit
*/
static ssize_t hnp_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
gotgctl_data_t val;
val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl));
return sprintf (buf, "HstNegScs = 0x%x\n", val.b.hstnegscs);
}
/**
* Set the HNP Request bit
*/
static ssize_t hnp_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t in = simple_strtoul(buf, NULL, 16);
uint32_t *addr = (uint32_t *)&(otg_dev->core_if->core_global_regs->gotgctl);
gotgctl_data_t mem;
mem.d32 = dwc_read_reg32(addr);
mem.b.hnpreq = in;
dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32);
dwc_write_reg32(addr, mem.d32);
return count;
}
DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store);
/**
* @todo Add code to initiate the SRP.
*/
/**
* Show the SRP status bit
*/
static ssize_t srp_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
#ifndef DWC_HOST_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
gotgctl_data_t val;
val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl));
return sprintf (buf, "SesReqScs = 0x%x\n", val.b.sesreqscs);
#else
return sprintf(buf, "Host Only Mode!\n");
#endif
}
/**
* Set the SRP Request bit
*/
static ssize_t srp_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
#ifndef DWC_HOST_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dwc_otg_pcd_initiate_srp(otg_dev->pcd);
#endif
return count;
}
DEVICE_ATTR(srp, 0644, srp_show, srp_store);
/**
* @todo Need to do more for power on/off?
*/
/**
* Show the Bus Power status
*/
static ssize_t buspower_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
hprt0_data_t val;
val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0);
return sprintf (buf, "Bus Power = 0x%x\n", val.b.prtpwr);
}
/**
* Set the Bus Power status
*/
static ssize_t buspower_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t on = simple_strtoul(buf, NULL, 16);
uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0;
hprt0_data_t mem;
mem.d32 = dwc_read_reg32(addr);
mem.b.prtpwr = on;
//dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32);
dwc_write_reg32(addr, mem.d32);
return count;
}
DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store);
/**
* @todo Need to do more for suspend?
*/
/**
* Show the Bus Suspend status
*/
static ssize_t bussuspend_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
hprt0_data_t val;
val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0);
return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp);
}
/**
* Set the Bus Suspend status
*/
static ssize_t bussuspend_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t in = simple_strtoul(buf, NULL, 16);
uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0;
hprt0_data_t mem;
mem.d32 = dwc_read_reg32(addr);
mem.b.prtsusp = in;
dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32);
dwc_write_reg32(addr, mem.d32);
return count;
}
DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store);
/**
* Show the status of Remote Wakeup.
*/
static ssize_t remote_wakeup_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
#ifndef DWC_HOST_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dctl_data_t val;
val.d32 =
dwc_read_reg32( &otg_dev->core_if->dev_if->dev_global_regs->dctl);
return sprintf( buf, "Remote Wakeup = %d Enabled = %d\n",
val.b.rmtwkupsig, otg_dev->pcd->remote_wakeup_enable);
#else
return sprintf(buf, "Host Only Mode!\n");
#endif
}
/**
* Initiate a remote wakeup of the host. The Device control register
* Remote Wakeup Signal bit is written if the PCD Remote wakeup enable
* flag is set.
*
*/
static ssize_t remote_wakeup_store( struct device *_dev,
struct device_attribute *attr,
const char *buf,
size_t count )
{
#ifndef DWC_HOST_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t val = simple_strtoul(buf, NULL, 16);
if (val&1) {
dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1);
}
else {
dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0);
}
#endif
return count;
}
DEVICE_ATTR(remote_wakeup, S_IRUGO|S_IWUSR, remote_wakeup_show,
remote_wakeup_store);
/**
* Dump global registers and either host or device registers (depending on the
* current mode of the core).
*/
static ssize_t regdump_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dwc_otg_dump_global_registers( otg_dev->core_if);
if (dwc_otg_is_host_mode(otg_dev->core_if)) {
dwc_otg_dump_host_registers( otg_dev->core_if);
} else {
dwc_otg_dump_dev_registers( otg_dev->core_if);
}
return sprintf( buf, "Register Dump\n" );
}
DEVICE_ATTR(regdump, S_IRUGO|S_IWUSR, regdump_show, 0);
/**
* Dump global registers and either host or device registers (depending on the
* current mode of the core).
*/
static ssize_t spramdump_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dwc_otg_dump_spram( otg_dev->core_if);
return sprintf( buf, "SPRAM Dump\n" );
}
DEVICE_ATTR(spramdump, S_IRUGO|S_IWUSR, spramdump_show, 0);
/**
* Dump the current hcd state.
*/
static ssize_t hcddump_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
#ifndef DWC_DEVICE_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dwc_otg_hcd_dump_state(otg_dev->hcd);
#endif
return sprintf( buf, "HCD Dump\n" );
}
DEVICE_ATTR(hcddump, S_IRUGO|S_IWUSR, hcddump_show, 0);
/**
* Dump the average frame remaining at SOF. This can be used to
* determine average interrupt latency. Frame remaining is also shown for
* start transfer and two additional sample points.
*/
static ssize_t hcd_frrem_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
#ifndef DWC_DEVICE_ONLY
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
dwc_otg_hcd_dump_frrem(otg_dev->hcd);
#endif
return sprintf( buf, "HCD Dump Frame Remaining\n" );
}
DEVICE_ATTR(hcd_frrem, S_IRUGO|S_IWUSR, hcd_frrem_show, 0);
/**
* Displays the time required to read the GNPTXFSIZ register many times (the
* output shows the number of times the register is read).
*/
#define RW_REG_COUNT 10000000
#define MSEC_PER_JIFFIE 1000/HZ
static ssize_t rd_reg_test_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
int i;
int time;
int start_jiffies;
printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
start_jiffies = jiffies;
for (i = 0; i < RW_REG_COUNT; i++) {
dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz);
}
time = jiffies - start_jiffies;
return sprintf( buf, "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
RW_REG_COUNT, time * MSEC_PER_JIFFIE, time );
}
DEVICE_ATTR(rd_reg_test, S_IRUGO|S_IWUSR, rd_reg_test_show, 0);
/**
* Displays the time required to write the GNPTXFSIZ register many times (the
* output shows the number of times the register is written).
*/
static ssize_t wr_reg_test_show( struct device *_dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = container_of(_dev, struct platform_device, dev); \
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); \
uint32_t reg_val;
int i;
int time;
int start_jiffies;
printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n",
HZ, MSEC_PER_JIFFIE, loops_per_jiffy);
reg_val = dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz);
start_jiffies = jiffies;
for (i = 0; i < RW_REG_COUNT; i++) {
dwc_write_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz, reg_val);
}
time = jiffies - start_jiffies;
return sprintf( buf, "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n",
RW_REG_COUNT, time * MSEC_PER_JIFFIE, time);
}
DEVICE_ATTR(wr_reg_test, S_IRUGO|S_IWUSR, wr_reg_test_show, 0);
/**@}*/
/**
* Create the device files
*/
void dwc_otg_attr_create (struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int error;
error = device_create_file(dev, &dev_attr_regoffset);
error = device_create_file(dev, &dev_attr_regvalue);
error = device_create_file(dev, &dev_attr_mode);
error = device_create_file(dev, &dev_attr_hnpcapable);
error = device_create_file(dev, &dev_attr_srpcapable);
error = device_create_file(dev, &dev_attr_hnp);
error = device_create_file(dev, &dev_attr_srp);
error = device_create_file(dev, &dev_attr_buspower);
error = device_create_file(dev, &dev_attr_bussuspend);
error = device_create_file(dev, &dev_attr_busconnected);
error = device_create_file(dev, &dev_attr_gotgctl);
error = device_create_file(dev, &dev_attr_gusbcfg);
error = device_create_file(dev, &dev_attr_grxfsiz);
error = device_create_file(dev, &dev_attr_gnptxfsiz);
error = device_create_file(dev, &dev_attr_gpvndctl);
error = device_create_file(dev, &dev_attr_ggpio);
error = device_create_file(dev, &dev_attr_guid);
error = device_create_file(dev, &dev_attr_gsnpsid);
error = device_create_file(dev, &dev_attr_devspeed);
error = device_create_file(dev, &dev_attr_enumspeed);
error = device_create_file(dev, &dev_attr_hptxfsiz);
error = device_create_file(dev, &dev_attr_hprt0);
error = device_create_file(dev, &dev_attr_remote_wakeup);
error = device_create_file(dev, &dev_attr_regdump);
error = device_create_file(dev, &dev_attr_spramdump);
error = device_create_file(dev, &dev_attr_hcddump);
error = device_create_file(dev, &dev_attr_hcd_frrem);
error = device_create_file(dev, &dev_attr_rd_reg_test);
error = device_create_file(dev, &dev_attr_wr_reg_test);
}
/**
* Remove the device files
*/
void dwc_otg_attr_remove (struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
device_remove_file(dev, &dev_attr_regoffset);
device_remove_file(dev, &dev_attr_regvalue);
device_remove_file(dev, &dev_attr_mode);
device_remove_file(dev, &dev_attr_hnpcapable);
device_remove_file(dev, &dev_attr_srpcapable);
device_remove_file(dev, &dev_attr_hnp);
device_remove_file(dev, &dev_attr_srp);
device_remove_file(dev, &dev_attr_buspower);
device_remove_file(dev, &dev_attr_bussuspend);
device_remove_file(dev, &dev_attr_busconnected);
device_remove_file(dev, &dev_attr_gotgctl);
device_remove_file(dev, &dev_attr_gusbcfg);
device_remove_file(dev, &dev_attr_grxfsiz);
device_remove_file(dev, &dev_attr_gnptxfsiz);
device_remove_file(dev, &dev_attr_gpvndctl);
device_remove_file(dev, &dev_attr_ggpio);
device_remove_file(dev, &dev_attr_guid);
device_remove_file(dev, &dev_attr_gsnpsid);
device_remove_file(dev, &dev_attr_devspeed);
device_remove_file(dev, &dev_attr_enumspeed);
device_remove_file(dev, &dev_attr_hptxfsiz);
device_remove_file(dev, &dev_attr_hprt0);
device_remove_file(dev, &dev_attr_remote_wakeup);
device_remove_file(dev, &dev_attr_regdump);
device_remove_file(dev, &dev_attr_spramdump);
device_remove_file(dev, &dev_attr_hcddump);
device_remove_file(dev, &dev_attr_hcd_frrem);
device_remove_file(dev, &dev_attr_rd_reg_test);
device_remove_file(dev, &dev_attr_wr_reg_test);
}

View File

@ -0,0 +1,67 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $
* $Revision: #7 $
* $Date: 2005/03/28 $
* $Change: 477051 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
* ========================================================================== */
#if !defined(__DWC_OTG_ATTR_H__)
#define __DWC_OTG_ATTR_H__
/** @file
* This file contains the interface to the Linux device attributes.
*/
extern struct device_attribute dev_attr_regoffset;
extern struct device_attribute dev_attr_regvalue;
extern struct device_attribute dev_attr_mode;
extern struct device_attribute dev_attr_hnpcapable;
extern struct device_attribute dev_attr_srpcapable;
extern struct device_attribute dev_attr_hnp;
extern struct device_attribute dev_attr_srp;
extern struct device_attribute dev_attr_buspower;
extern struct device_attribute dev_attr_bussuspend;
extern struct device_attribute dev_attr_busconnected;
extern struct device_attribute dev_attr_gotgctl;
extern struct device_attribute dev_attr_gusbcfg;
extern struct device_attribute dev_attr_grxfsiz;
extern struct device_attribute dev_attr_gnptxfsiz;
extern struct device_attribute dev_attr_gpvndctl;
extern struct device_attribute dev_attr_ggpio;
extern struct device_attribute dev_attr_guid;
extern struct device_attribute dev_attr_gsnpsid;
extern struct device_attribute dev_attr_devspeed;
extern struct device_attribute dev_attr_enumspeed;
extern struct device_attribute dev_attr_hptxfsiz;
extern struct device_attribute dev_attr_hprt0;
void dwc_otg_attr_create (struct platform_device *pdev);
void dwc_otg_attr_remove (struct platform_device *pdev);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,852 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $
* $Revision: #10 $
* $Date: 2008/07/16 $
* $Change: 1065567 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
* ========================================================================== */
/** @file
*
* The Core Interface Layer provides basic services for accessing and
* managing the DWC_otg hardware. These services are used by both the
* Host Controller Driver and the Peripheral Controller Driver.
*
* This file contains the Common Interrupt handlers.
*/
#include "otg_plat.h"
#include "otg_regs.h"
#include "otg_cil.h"
#include "otg_pcd.h"
#ifdef DEBUG
inline const char *op_state_str(dwc_otg_core_if_t *core_if)
{
return (core_if->op_state==A_HOST?"a_host":
(core_if->op_state==A_SUSPEND?"a_suspend":
(core_if->op_state==A_PERIPHERAL?"a_peripheral":
(core_if->op_state==B_PERIPHERAL?"b_peripheral":
(core_if->op_state==B_HOST?"b_host":
"unknown")))));
}
#endif
/** This function will log a debug message
*
* @param core_if Programming view of DWC_otg controller.
*/
int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *core_if)
{
gintsts_data_t gintsts;
DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n",
dwc_otg_mode(core_if) ? "Host" : "Device");
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.modemismatch = 1;
dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
/** Start the HCD. Helper function for using the HCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void hcd_start(dwc_otg_core_if_t *core_if)
{
if (core_if->hcd_cb && core_if->hcd_cb->start) {
core_if->hcd_cb->start(core_if->hcd_cb->p);
}
}
/** Stop the HCD. Helper function for using the HCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void hcd_stop(dwc_otg_core_if_t *core_if)
{
if (core_if->hcd_cb && core_if->hcd_cb->stop) {
core_if->hcd_cb->stop(core_if->hcd_cb->p);
}
}
/** Disconnect the HCD. Helper function for using the HCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void hcd_disconnect(dwc_otg_core_if_t *core_if)
{
if (core_if->hcd_cb && core_if->hcd_cb->disconnect) {
core_if->hcd_cb->disconnect(core_if->hcd_cb->p);
}
}
/** Inform the HCD the a New Session has begun. Helper function for
* using the HCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void hcd_session_start(dwc_otg_core_if_t *core_if)
{
if (core_if->hcd_cb && core_if->hcd_cb->session_start) {
core_if->hcd_cb->session_start(core_if->hcd_cb->p);
}
}
/** Start the PCD. Helper function for using the PCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void pcd_start(dwc_otg_core_if_t *core_if)
{
if (core_if->pcd_cb && core_if->pcd_cb->start) {
core_if->pcd_cb->start(core_if->pcd_cb->p);
}
}
/** Stop the PCD. Helper function for using the PCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void pcd_stop(dwc_otg_core_if_t *core_if)
{
if (core_if->pcd_cb && core_if->pcd_cb->stop) {
core_if->pcd_cb->stop(core_if->pcd_cb->p);
}
}
/** Suspend the PCD. Helper function for using the PCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void pcd_suspend(dwc_otg_core_if_t *core_if)
{
if (core_if->pcd_cb && core_if->pcd_cb->suspend) {
core_if->pcd_cb->suspend(core_if->pcd_cb->p);
}
}
/** Resume the PCD. Helper function for using the PCD callbacks.
*
* @param core_if Programming view of DWC_otg controller.
*/
static inline void pcd_resume(dwc_otg_core_if_t *core_if)
{
if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
}
}
/**
* This function handles the OTG Interrupts. It reads the OTG
* Interrupt Register (GOTGINT) to determine what interrupt has
* occurred.
*
* @param core_if Programming view of DWC_otg controller.
*/
int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *core_if)
{
dwc_otg_core_global_regs_t *global_regs =
core_if->core_global_regs;
gotgint_data_t gotgint;
gotgctl_data_t gotgctl;
gintmsk_data_t gintmsk;
gotgint.d32 = dwc_read_reg32(&global_regs->gotgint);
gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32,
op_state_str(core_if));
//DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32);
if (gotgint.b.sesenddet) {
DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
"Session End Detected++ (%s)\n",
op_state_str(core_if));
gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
if (core_if->op_state == B_HOST) {
dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_start(core_if);
SPIN_UNLOCK(&pcd->lock);
core_if->op_state = B_PERIPHERAL;
} else {
dwc_otg_pcd_t *pcd;
/* If not B_HOST and Device HNP still set. HNP
* Did not succeed!*/
if (gotgctl.b.devhnpen) {
DWC_DEBUGPL(DBG_ANY, "Session End Detected\n");
DWC_ERROR("Device Not Connected/Responding!\n");
}
/* If Session End Detected the B-Cable has
* been disconnected. */
/* Reset PCD and Gadget driver to a
* clean state. */
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_stop(core_if);
SPIN_UNLOCK(&pcd->lock);
}
gotgctl.d32 = 0;
gotgctl.b.devhnpen = 1;
dwc_modify_reg32(&global_regs->gotgctl,
gotgctl.d32, 0);
}
if (gotgint.b.sesreqsucstschng) {
DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
"Session Reqeust Success Status Change++\n");
gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
if (gotgctl.b.sesreqscs) {
if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) &&
(core_if->core_params->i2c_enable)) {
core_if->srp_success = 1;
}
else {
dwc_otg_pcd_t *pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_resume(core_if);
SPIN_UNLOCK(&pcd->lock);
/* Clear Session Request */
gotgctl.d32 = 0;
gotgctl.b.sesreq = 1;
dwc_modify_reg32(&global_regs->gotgctl,
gotgctl.d32, 0);
}
}
}
if (gotgint.b.hstnegsucstschng) {
/* Print statements during the HNP interrupt handling
* can cause it to fail.*/
gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl);
if (gotgctl.b.hstnegscs) {
if (dwc_otg_is_host_mode(core_if)) {
dwc_otg_pcd_t *pcd;
core_if->op_state = B_HOST;
/*
* Need to disable SOF interrupt immediately.
* When switching from device to host, the PCD
* interrupt handler won't handle the
* interrupt if host mode is already set. The
* HCD interrupt handler won't get called if
* the HCD state is HALT. This means that the
* interrupt does not get handled and Linux
* complains loudly.
*/
gintmsk.d32 = 0;
gintmsk.b.sofintr = 1;
dwc_modify_reg32(&global_regs->gintmsk,
gintmsk.d32, 0);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_stop(core_if);
SPIN_UNLOCK(&pcd->lock);
/*
* Initialize the Core for Host mode.
*/
hcd_start(core_if);
core_if->op_state = B_HOST;
}
} else {
gotgctl.d32 = 0;
gotgctl.b.hnpreq = 1;
gotgctl.b.devhnpen = 1;
dwc_modify_reg32(&global_regs->gotgctl,
gotgctl.d32, 0);
DWC_DEBUGPL(DBG_ANY, "HNP Failed\n");
DWC_ERROR("Device Not Connected/Responding\n");
}
}
if (gotgint.b.hstnegdet) {
/* The disconnect interrupt is set at the same time as
* Host Negotiation Detected. During the mode
* switch all interrupts are cleared so the disconnect
* interrupt handler will not get executed.
*/
DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
"Host Negotiation Detected++ (%s)\n",
(dwc_otg_is_host_mode(core_if)?"Host":"Device"));
if (dwc_otg_is_device_mode(core_if)){
dwc_otg_pcd_t *pcd;
DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", core_if->op_state);
hcd_disconnect(core_if);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_start(core_if);
SPIN_UNLOCK(&pcd->lock);
core_if->op_state = A_PERIPHERAL;
} else {
dwc_otg_pcd_t *pcd;
/*
* Need to disable SOF interrupt immediately. When
* switching from device to host, the PCD interrupt
* handler won't handle the interrupt if host mode is
* already set. The HCD interrupt handler won't get
* called if the HCD state is HALT. This means that
* the interrupt does not get handled and Linux
* complains loudly.
*/
gintmsk.d32 = 0;
gintmsk.b.sofintr = 1;
dwc_modify_reg32(&global_regs->gintmsk,
gintmsk.d32, 0);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_stop(core_if);
SPIN_UNLOCK(&pcd->lock);
hcd_start(core_if);
core_if->op_state = A_HOST;
}
}
if (gotgint.b.adevtoutchng) {
DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
"A-Device Timeout Change++\n");
}
if (gotgint.b.debdone) {
DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: "
"Debounce Done++\n");
}
/* Clear GOTGINT */
dwc_write_reg32 (&core_if->core_global_regs->gotgint, gotgint.d32);
return 1;
}
void w_conn_id_status_change(struct work_struct *p)
{
dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id);
uint32_t count = 0;
gotgctl_data_t gotgctl = { .d32 = 0 };
gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32);
DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts);
/* B-Device connector (Device Mode) */
if (gotgctl.b.conidsts) {
dwc_otg_pcd_t *pcd;
/* Wait for switch to device mode. */
while (!dwc_otg_is_device_mode(core_if)){
DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n",
(dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
MDELAY(100);
if (++count > 10000) *(uint32_t*)NULL=0;
}
core_if->op_state = B_PERIPHERAL;
dwc_otg_core_init(core_if);
dwc_otg_enable_global_interrupts(core_if);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_start(core_if);
SPIN_UNLOCK(&pcd->lock);
} else {
/* A-Device connector (Host Mode) */
while (!dwc_otg_is_host_mode(core_if)) {
DWC_PRINT("Waiting for Host Mode, Mode=%s\n",
(dwc_otg_is_host_mode(core_if)?"Host":"Peripheral"));
MDELAY(100);
if (++count > 10000) *(uint32_t*)NULL=0;
}
core_if->op_state = A_HOST;
/*
* Initialize the Core for Host mode.
*/
dwc_otg_core_init(core_if);
dwc_otg_enable_global_interrupts(core_if);
hcd_start(core_if);
}
}
/**
* This function handles the Connector ID Status Change Interrupt. It
* reads the OTG Interrupt Register (GOTCTL) to determine whether this
* is a Device to Host Mode transition or a Host Mode to Device
* Transition.
*
* This only occurs when the cable is connected/removed from the PHY
* connector.
*
* @param core_if Programming view of DWC_otg controller.
*/
int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if)
{
/*
* Need to disable SOF interrupt immediately. If switching from device
* to host, the PCD interrupt handler won't handle the interrupt if
* host mode is already set. The HCD interrupt handler won't get
* called if the HCD state is HALT. This means that the interrupt does
* not get handled and Linux complains loudly.
*/
gintmsk_data_t gintmsk = { .d32 = 0 };
gintsts_data_t gintsts = { .d32 = 0 };
gintmsk.b.sofintr = 1;
dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0);
DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n",
(dwc_otg_is_host_mode(core_if)?"Host":"Device"));
/*
* Need to schedule a work, as there are possible DELAY function calls
*/
queue_work(core_if->wq_otg, &core_if->w_conn_id);
/* Set flag and clear interrupt */
gintsts.b.conidstschng = 1;
dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
/**
* This interrupt indicates that a device is initiating the Session
* Request Protocol to request the host to turn on bus power so a new
* session can begin. The handler responds by turning on bus power. If
* the DWC_otg controller is in low power mode, the handler brings the
* controller out of low power mode before turning on bus power.
*
* @param core_if Programming view of DWC_otg controller.
*/
int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if)
{
hprt0_data_t hprt0;
gintsts_data_t gintsts;
#ifndef DWC_HOST_ONLY
DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n");
if (dwc_otg_is_device_mode(core_if)) {
DWC_PRINT("SRP: Device mode\n");
} else {
DWC_PRINT("SRP: Host mode\n");
/* Turn on the port power bit. */
hprt0.d32 = dwc_otg_read_hprt0(core_if);
hprt0.b.prtpwr = 1;
dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
/* Start the Connection timer. So a message can be displayed
* if connect does not occur within 10 seconds. */
hcd_session_start(core_if);
}
#endif
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.sessreqintr = 1;
dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
void w_wakeup_detected(struct work_struct *p)
{
struct delayed_work *dw = container_of(p, struct delayed_work, work);
dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp);
/*
* Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms
* so that OPT tests pass with all PHYs).
*/
hprt0_data_t hprt0 = {.d32=0};
hprt0.d32 = dwc_otg_read_hprt0(core_if);
DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32);
// MDELAY(70);
hprt0.b.prtres = 0; /* Resume */
dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32);
DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0));
}
/**
* This interrupt indicates that the DWC_otg controller has detected a
* resume or remote wakeup sequence. If the DWC_otg controller is in
* low power mode, the handler must brings the controller out of low
* power mode. The controller automatically begins resume
* signaling. The handler schedules a time to stop resume signaling.
*/
int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if)
{
gintsts_data_t gintsts;
DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n");
if (dwc_otg_is_device_mode(core_if)) {
dctl_data_t dctl = {.d32=0};
DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n",
dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts));
#ifdef PARTIAL_POWER_DOWN
if (core_if->hwcfg4.b.power_optimiz) {
pcgcctl_data_t power = {.d32=0};
power.d32 = dwc_read_reg32(core_if->pcgcctl);
DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32);
power.b.stoppclk = 0;
dwc_write_reg32(core_if->pcgcctl, power.d32);
power.b.pwrclmp = 0;
dwc_write_reg32(core_if->pcgcctl, power.d32);
power.b.rstpdwnmodule = 0;
dwc_write_reg32(core_if->pcgcctl, power.d32);
}
#endif
/* Clear the Remote Wakeup Signalling */
dctl.b.rmtwkupsig = 1;
dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl,
dctl.d32, 0);
if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) {
core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p);
}
} else {
pcgcctl_data_t pcgcctl = {.d32=0};
/* Restart the Phy Clock */
pcgcctl.b.stoppclk = 1;
dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0);
queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1));
}
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.wkupintr = 1;
dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
/**
* This interrupt indicates that a device has been disconnected from
* the root port.
*/
int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if)
{
gintsts_data_t gintsts;
DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n",
(dwc_otg_is_host_mode(core_if)?"Host":"Device"),
op_state_str(core_if));
/** @todo Consolidate this if statement. */
#ifndef DWC_HOST_ONLY
if (core_if->op_state == B_HOST) {
dwc_otg_pcd_t *pcd;
/* If in device mode Disconnect and stop the HCD, then
* start the PCD. */
hcd_disconnect(core_if);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_start(core_if);
SPIN_UNLOCK(&pcd->lock);
core_if->op_state = B_PERIPHERAL;
} else if (dwc_otg_is_device_mode(core_if)) {
gotgctl_data_t gotgctl = { .d32 = 0 };
gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl);
if (gotgctl.b.hstsethnpen==1) {
/* Do nothing, if HNP in process the OTG
* interrupt "Host Negotiation Detected"
* interrupt will do the mode switch.
*/
} else if (gotgctl.b.devhnpen == 0) {
dwc_otg_pcd_t *pcd;
/* If in device mode Disconnect and stop the HCD, then
* start the PCD. */
hcd_disconnect(core_if);
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_start(core_if);
SPIN_UNLOCK(&pcd->lock);
core_if->op_state = B_PERIPHERAL;
} else {
DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n");
}
} else {
if (core_if->op_state == A_HOST) {
/* A-Cable still connected but device disconnected. */
hcd_disconnect(core_if);
}
}
#endif
gintsts.d32 = 0;
gintsts.b.disconnect = 1;
dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
/**
* This interrupt indicates that SUSPEND state has been detected on
* the USB.
*
* For HNP the USB Suspend interrupt signals the change from
* "a_peripheral" to "a_host".
*
* When power management is enabled the core will be put in low power
* mode.
*/
int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if)
{
dsts_data_t dsts;
gintsts_data_t gintsts;
DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n");
if (dwc_otg_is_device_mode(core_if)) {
dwc_otg_pcd_t *pcd;
/* Check the Device status register to determine if the Suspend
* state is active. */
dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts);
DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32);
DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d "
"HWCFG4.power Optimize=%d\n",
dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz);
#ifdef PARTIAL_POWER_DOWN
/** @todo Add a module parameter for power management. */
if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) {
pcgcctl_data_t power = {.d32=0};
DWC_DEBUGPL(DBG_CIL, "suspend\n");
power.b.pwrclmp = 1;
dwc_write_reg32(core_if->pcgcctl, power.d32);
power.b.rstpdwnmodule = 1;
dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
power.b.stoppclk = 1;
dwc_modify_reg32(core_if->pcgcctl, 0, power.d32);
} else {
DWC_DEBUGPL(DBG_ANY,"disconnect?\n");
}
#endif
/* PCD callback for suspend. */
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_suspend(core_if);
SPIN_UNLOCK(&pcd->lock);
} else {
if (core_if->op_state == A_PERIPHERAL) {
dwc_otg_pcd_t *pcd;
DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n");
/* Clear the a_peripheral flag, back to a_host. */
pcd=(dwc_otg_pcd_t *)core_if->pcd_cb->p;
if(unlikely(!pcd)) {
DWC_ERROR("%s: data structure not initialized properly, core_if->pcd_cb->p = NULL!!!",__func__);
BUG();
}
SPIN_LOCK(&pcd->lock);
pcd_stop(core_if);
SPIN_UNLOCK(&pcd->lock);
hcd_start(core_if);
core_if->op_state = A_HOST;
}
}
/* Clear interrupt */
gintsts.d32 = 0;
gintsts.b.usbsuspend = 1;
dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32);
return 1;
}
/**
* This function returns the Core Interrupt register.
*/
static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *core_if)
{
gintsts_data_t gintsts;
gintmsk_data_t gintmsk;
gintmsk_data_t gintmsk_common = {.d32=0};
gintmsk_common.b.wkupintr = 1;
gintmsk_common.b.sessreqintr = 1;
gintmsk_common.b.conidstschng = 1;
gintmsk_common.b.otgintr = 1;
gintmsk_common.b.modemismatch = 1;
gintmsk_common.b.disconnect = 1;
gintmsk_common.b.usbsuspend = 1;
/** @todo: The port interrupt occurs while in device
* mode. Added code to CIL to clear the interrupt for now!
*/
gintmsk_common.b.portintr = 1;
gintsts.d32 = dwc_read_reg32(&core_if->core_global_regs->gintsts);
gintmsk.d32 = dwc_read_reg32(&core_if->core_global_regs->gintmsk);
#ifdef DEBUG
/* if any common interrupts set */
if (gintsts.d32 & gintmsk_common.d32) {
DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n",
gintsts.d32, gintmsk.d32);
}
#endif
return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32);
}
/**
* Common interrupt handler.
*
* The common interrupts are those that occur in both Host and Device mode.
* This handler handles the following interrupts:
* - Mode Mismatch Interrupt
* - Disconnect Interrupt
* - OTG Interrupt
* - Connector ID Status Change Interrupt
* - Session Request Interrupt.
* - Resume / Remote Wakeup Detected Interrupt.
*
*/
int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t *core_if)
{
int retval = 0;
gintsts_data_t gintsts;
gintsts.d32 = dwc_otg_read_common_intr(core_if);
if (gintsts.b.modemismatch) {
retval |= dwc_otg_handle_mode_mismatch_intr(core_if);
}
if (gintsts.b.otgintr) {
retval |= dwc_otg_handle_otg_intr(core_if);
}
if (gintsts.b.conidstschng) {
retval |= dwc_otg_handle_conn_id_status_change_intr(core_if);
}
if (gintsts.b.disconnect) {
retval |= dwc_otg_handle_disconnect_intr(core_if);
}
if (gintsts.b.sessreqintr) {
retval |= dwc_otg_handle_session_req_intr(core_if);
}
if (gintsts.b.wkupintr) {
retval |= dwc_otg_handle_wakeup_detected_intr(core_if);
}
if (gintsts.b.usbsuspend) {
retval |= dwc_otg_handle_usb_suspend_intr(core_if);
}
if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) {
/* The port interrupt occurs while in device mode with HPRT0
* Port Enable/Disable.
*/
gintsts.d32 = 0;
gintsts.b.portintr = 1;
dwc_write_reg32(&core_if->core_global_regs->gintsts,
gintsts.d32);
retval |= 1;
}
S3C2410X_CLEAR_EINTPEND();
return retval;
}

View File

@ -0,0 +1,965 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $
* $Revision: #63 $
* $Date: 2008/09/24 $
* $Change: 1101777 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
* ========================================================================== */
/** @file
* The dwc_otg_driver module provides the initialization and cleanup entry
* points for the DWC_otg driver. This module will be dynamically installed
* after Linux is booted using the insmod command. When the module is
* installed, the dwc_otg_driver_init function is called. When the module is
* removed (using rmmod), the dwc_otg_driver_cleanup function is called.
*
* This module also defines a data structure for the dwc_otg_driver, which is
* used in conjunction with the standard ARM lm_device structure. These
* structures allow the OTG driver to comply with the standard Linux driver
* model in which devices and drivers are registered with a bus driver. This
* has the benefit that Linux can expose attributes of the driver and device
* in its special sysfs file system. Users can then read or write files in
* this file system to perform diagnostics on the driver components or the
* device.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/stat.h> /* permission constants */
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include <mach/pm.h>
#include "otg_plat.h"
#include "otg_attr.h"
#include "otg_driver.h"
#include "otg_cil.h"
#include "otg_pcd.h"
#include "otg_hcd.h"
#define DWC_DRIVER_VERSION "2.72a 24-JUN-2008"
#define DWC_DRIVER_DESC "HS OTG USB Controller driver"
static const char dwc_driver_name[] = "dwc_otg";
/*-------------------------------------------------------------------------*/
/* Encapsulate the module parameter settings */
static dwc_otg_core_params_t dwc_otg_module_params = {
.opt = -1,
.otg_cap = -1,
.dma_enable = -1,
.dma_desc_enable = -1,
.dma_burst_size = -1,
.speed = -1,
.host_support_fs_ls_low_power = -1,
.host_ls_low_power_phy_clk = -1,
.enable_dynamic_fifo = -1,
.data_fifo_size = -1,
.dev_rx_fifo_size = -1,
.dev_nperio_tx_fifo_size = -1,
.dev_perio_tx_fifo_size = {
/* dev_perio_tx_fifo_size_1 */
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1
/* 15 */
},
.host_rx_fifo_size = -1,
.host_nperio_tx_fifo_size = -1,
.host_perio_tx_fifo_size = -1,
.max_transfer_size = -1,
.max_packet_count = -1,
.host_channels = -1,
.dev_endpoints = -1,
.phy_type = -1,
.phy_utmi_width = -1,
.phy_ulpi_ddr = -1,
.phy_ulpi_ext_vbus = -1,
.i2c_enable = -1,
.ulpi_fs_ls = -1,
.ts_dline = -1,
.en_multiple_tx_fifo = -1,
.dev_tx_fifo_size = {
/* dev_tx_fifo_size */
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1,
-1
/* 15 */
},
.thr_ctl = -1,
.tx_thr_length = -1,
.rx_thr_length = -1,
.pti_enable = -1,
.mpi_enable = -1,
};
/**
* Global Debug Level Mask.
*/
uint32_t g_dbg_lvl = 0; /* OFF */
/**
* This function is called during module intialization to verify that
* the module parameters are in a valid state.
*/
static int check_parameters(dwc_otg_core_if_t *core_if)
{
int i;
int retval = 0;
/* Checks if the parameter is outside of its valid range of values */
#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \
((dwc_otg_module_params._param_ < (_low_)) || \
(dwc_otg_module_params._param_ > (_high_)))
/* If the parameter has been set by the user, check that the parameter value is
* within the value range of values. If not, report a module error. */
#define DWC_OTG_PARAM_ERR(_param_, _low_, _high_, _string_) \
do { \
if (dwc_otg_module_params._param_ != -1) { \
if (DWC_OTG_PARAM_TEST(_param_, (_low_), (_high_))) { \
DWC_ERROR("`%d' invalid for parameter `%s'\n", \
dwc_otg_module_params._param_, _string_); \
dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \
retval++; \
} \
} \
} while (0)
DWC_OTG_PARAM_ERR(opt,0,1,"opt");
DWC_OTG_PARAM_ERR(otg_cap,0,2,"otg_cap");
DWC_OTG_PARAM_ERR(dma_enable,0,1,"dma_enable");
DWC_OTG_PARAM_ERR(dma_desc_enable,0,1,"dma_desc_enable");
DWC_OTG_PARAM_ERR(speed,0,1,"speed");
DWC_OTG_PARAM_ERR(host_support_fs_ls_low_power,0,1,"host_support_fs_ls_low_power");
DWC_OTG_PARAM_ERR(host_ls_low_power_phy_clk,0,1,"host_ls_low_power_phy_clk");
DWC_OTG_PARAM_ERR(enable_dynamic_fifo,0,1,"enable_dynamic_fifo");
DWC_OTG_PARAM_ERR(data_fifo_size,32,32768,"data_fifo_size");
DWC_OTG_PARAM_ERR(dev_rx_fifo_size,16,32768,"dev_rx_fifo_size");
DWC_OTG_PARAM_ERR(dev_nperio_tx_fifo_size,16,32768,"dev_nperio_tx_fifo_size");
DWC_OTG_PARAM_ERR(host_rx_fifo_size,16,32768,"host_rx_fifo_size");
DWC_OTG_PARAM_ERR(host_nperio_tx_fifo_size,16,32768,"host_nperio_tx_fifo_size");
DWC_OTG_PARAM_ERR(host_perio_tx_fifo_size,16,32768,"host_perio_tx_fifo_size");
DWC_OTG_PARAM_ERR(max_transfer_size,2047,524288,"max_transfer_size");
DWC_OTG_PARAM_ERR(max_packet_count,15,511,"max_packet_count");
DWC_OTG_PARAM_ERR(host_channels,1,16,"host_channels");
DWC_OTG_PARAM_ERR(dev_endpoints,1,15,"dev_endpoints");
DWC_OTG_PARAM_ERR(phy_type,0,2,"phy_type");
DWC_OTG_PARAM_ERR(phy_ulpi_ddr,0,1,"phy_ulpi_ddr");
DWC_OTG_PARAM_ERR(phy_ulpi_ext_vbus,0,1,"phy_ulpi_ext_vbus");
DWC_OTG_PARAM_ERR(i2c_enable,0,1,"i2c_enable");
DWC_OTG_PARAM_ERR(ulpi_fs_ls,0,1,"ulpi_fs_ls");
DWC_OTG_PARAM_ERR(ts_dline,0,1,"ts_dline");
if (dwc_otg_module_params.dma_burst_size != -1) {
if (DWC_OTG_PARAM_TEST(dma_burst_size,1,1) &&
DWC_OTG_PARAM_TEST(dma_burst_size,4,4) &&
DWC_OTG_PARAM_TEST(dma_burst_size,8,8) &&
DWC_OTG_PARAM_TEST(dma_burst_size,16,16) &&
DWC_OTG_PARAM_TEST(dma_burst_size,32,32) &&
DWC_OTG_PARAM_TEST(dma_burst_size,64,64) &&
DWC_OTG_PARAM_TEST(dma_burst_size,128,128) &&
DWC_OTG_PARAM_TEST(dma_burst_size,256,256)) {
DWC_ERROR("`%d' invalid for parameter `dma_burst_size'\n",
dwc_otg_module_params.dma_burst_size);
dwc_otg_module_params.dma_burst_size = 32;
retval++;
}
{
uint8_t brst_sz = 0;
while(dwc_otg_module_params.dma_burst_size > 1) {
brst_sz ++;
dwc_otg_module_params.dma_burst_size >>= 1;
}
dwc_otg_module_params.dma_burst_size = brst_sz;
}
}
if (dwc_otg_module_params.phy_utmi_width != -1) {
if (DWC_OTG_PARAM_TEST(phy_utmi_width, 8, 8) &&
DWC_OTG_PARAM_TEST(phy_utmi_width, 16, 16)) {
DWC_ERROR("`%d' invalid for parameter `phy_utmi_width'\n",
dwc_otg_module_params.phy_utmi_width);
dwc_otg_module_params.phy_utmi_width = 16;
retval++;
}
}
for (i = 0; i < 15; i++) {
/** @todo should be like above */
//DWC_OTG_PARAM_ERR(dev_perio_tx_fifo_size[i], 4, 768, "dev_perio_tx_fifo_size");
if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) {
if (DWC_OTG_PARAM_TEST(dev_perio_tx_fifo_size[i], 4, 768)) {
DWC_ERROR("`%d' invalid for parameter `%s_%d'\n",
dwc_otg_module_params.dev_perio_tx_fifo_size[i], "dev_perio_tx_fifo_size", i);
dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default;
retval++;
}
}
}
DWC_OTG_PARAM_ERR(en_multiple_tx_fifo, 0, 1, "en_multiple_tx_fifo");
for (i = 0; i < 15; i++) {
/** @todo should be like above */
//DWC_OTG_PARAM_ERR(dev_tx_fifo_size[i], 4, 768, "dev_tx_fifo_size");
if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) {
if (DWC_OTG_PARAM_TEST(dev_tx_fifo_size[i], 4, 768)) {
DWC_ERROR("`%d' invalid for parameter `%s_%d'\n",
dwc_otg_module_params.dev_tx_fifo_size[i], "dev_tx_fifo_size", i);
dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default;
retval++;
}
}
}
DWC_OTG_PARAM_ERR(thr_ctl, 0, 7, "thr_ctl");
DWC_OTG_PARAM_ERR(tx_thr_length, 8, 128, "tx_thr_length");
DWC_OTG_PARAM_ERR(rx_thr_length, 8, 128, "rx_thr_length");
DWC_OTG_PARAM_ERR(pti_enable,0,1,"pti_enable");
DWC_OTG_PARAM_ERR(mpi_enable,0,1,"mpi_enable");
/* At this point, all module parameters that have been set by the user
* are valid, and those that have not are left unset. Now set their
* default values and/or check the parameters against the hardware
* configurations of the OTG core. */
/* This sets the parameter to the default value if it has not been set by the
* user */
#define DWC_OTG_PARAM_SET_DEFAULT(_param_) \
({ \
int changed = 1; \
if (dwc_otg_module_params._param_ == -1) { \
changed = 0; \
dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \
} \
changed; \
})
/* This checks the macro agains the hardware configuration to see if it is
* valid. It is possible that the default value could be invalid. In this
* case, it will report a module error if the user touched the parameter.
* Otherwise it will adjust the value without any error. */
#define DWC_OTG_PARAM_CHECK_VALID(_param_, _str_, _is_valid_, _set_valid_) \
({ \
int changed = DWC_OTG_PARAM_SET_DEFAULT(_param_); \
int error = 0; \
if (!(_is_valid_)) { \
if (changed) { \
DWC_ERROR("`%d' invalid for parameter `%s'. Check HW configuration.\n", dwc_otg_module_params._param_, _str_); \
error = 1; \
} \
dwc_otg_module_params._param_ = (_set_valid_); \
} \
error; \
})
/* OTG Cap */
retval += DWC_OTG_PARAM_CHECK_VALID(otg_cap, "otg_cap",
({
int valid;
valid = 1;
switch (dwc_otg_module_params.otg_cap) {
case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE:
if (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG)
valid = 0;
break;
case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE:
if ((core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) &&
(core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) &&
(core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) &&
(core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) {
valid = 0;
}
break;
case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE:
/* always valid */
break;
}
valid;
}),
(((core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ||
(core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ||
(core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
(core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ?
DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE :
DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE));
retval += DWC_OTG_PARAM_CHECK_VALID(dma_enable, "dma_enable",
((dwc_otg_module_params.dma_enable == 1) && (core_if->hwcfg2.b.architecture == 0)) ? 0 : 1,
0);
retval += DWC_OTG_PARAM_CHECK_VALID(dma_desc_enable, "dma_desc_enable",
((dwc_otg_module_params.dma_desc_enable == 1) &&
((dwc_otg_module_params.dma_enable == 0) || (core_if->hwcfg4.b.desc_dma == 0))) ? 0 : 1,
0);
retval += DWC_OTG_PARAM_CHECK_VALID(opt, "opt", 1, 0);
DWC_OTG_PARAM_SET_DEFAULT(dma_burst_size);
retval += DWC_OTG_PARAM_CHECK_VALID(host_support_fs_ls_low_power,
"host_support_fs_ls_low_power",
1, 0);
retval += DWC_OTG_PARAM_CHECK_VALID(enable_dynamic_fifo,
"enable_dynamic_fifo",
((dwc_otg_module_params.enable_dynamic_fifo == 0) ||
(core_if->hwcfg2.b.dynamic_fifo == 1)), 0);
retval += DWC_OTG_PARAM_CHECK_VALID(data_fifo_size,
"data_fifo_size",
(dwc_otg_module_params.data_fifo_size <= core_if->hwcfg3.b.dfifo_depth),
core_if->hwcfg3.b.dfifo_depth);
retval += DWC_OTG_PARAM_CHECK_VALID(dev_rx_fifo_size,
"dev_rx_fifo_size",
(dwc_otg_module_params.dev_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)),
dwc_read_reg32(&core_if->core_global_regs->grxfsiz));
retval += DWC_OTG_PARAM_CHECK_VALID(dev_nperio_tx_fifo_size,
"dev_nperio_tx_fifo_size",
(dwc_otg_module_params.dev_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)),
(dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16));
retval += DWC_OTG_PARAM_CHECK_VALID(host_rx_fifo_size,
"host_rx_fifo_size",
(dwc_otg_module_params.host_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)),
dwc_read_reg32(&core_if->core_global_regs->grxfsiz));
retval += DWC_OTG_PARAM_CHECK_VALID(host_nperio_tx_fifo_size,
"host_nperio_tx_fifo_size",
(dwc_otg_module_params.host_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)),
(dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16));
retval += DWC_OTG_PARAM_CHECK_VALID(host_perio_tx_fifo_size,
"host_perio_tx_fifo_size",
(dwc_otg_module_params.host_perio_tx_fifo_size <= ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))),
((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16)));
retval += DWC_OTG_PARAM_CHECK_VALID(max_transfer_size,
"max_transfer_size",
(dwc_otg_module_params.max_transfer_size < (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))),
((1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11)) - 1));
retval += DWC_OTG_PARAM_CHECK_VALID(max_packet_count,
"max_packet_count",
(dwc_otg_module_params.max_packet_count < (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))),
((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1));
retval += DWC_OTG_PARAM_CHECK_VALID(host_channels,
"host_channels",
(dwc_otg_module_params.host_channels <= (core_if->hwcfg2.b.num_host_chan + 1)),
(core_if->hwcfg2.b.num_host_chan + 1));
retval += DWC_OTG_PARAM_CHECK_VALID(dev_endpoints,
"dev_endpoints",
(dwc_otg_module_params.dev_endpoints <= (core_if->hwcfg2.b.num_dev_ep)),
core_if->hwcfg2.b.num_dev_ep);
/*
* Define the following to disable the FS PHY Hardware checking. This is for
* internal testing only.
*
* #define NO_FS_PHY_HW_CHECKS
*/
#ifdef NO_FS_PHY_HW_CHECKS
retval += DWC_OTG_PARAM_CHECK_VALID(phy_type,
"phy_type", 1, 0);
#else
retval += DWC_OTG_PARAM_CHECK_VALID(phy_type,
"phy_type",
({
int valid = 0;
if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_UTMI) &&
((core_if->hwcfg2.b.hs_phy_type == 1) ||
(core_if->hwcfg2.b.hs_phy_type == 3))) {
valid = 1;
}
else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_ULPI) &&
((core_if->hwcfg2.b.hs_phy_type == 2) ||
(core_if->hwcfg2.b.hs_phy_type == 3))) {
valid = 1;
}
else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) &&
(core_if->hwcfg2.b.fs_phy_type == 1)) {
valid = 1;
}
valid;
}),
({
int set = DWC_PHY_TYPE_PARAM_FS;
if (core_if->hwcfg2.b.hs_phy_type) {
if ((core_if->hwcfg2.b.hs_phy_type == 3) ||
(core_if->hwcfg2.b.hs_phy_type == 1)) {
set = DWC_PHY_TYPE_PARAM_UTMI;
}
else {
set = DWC_PHY_TYPE_PARAM_ULPI;
}
}
set;
}));
#endif
retval += DWC_OTG_PARAM_CHECK_VALID(speed, "speed",
(dwc_otg_module_params.speed == 0) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1,
dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS ? 1 : 0);
retval += DWC_OTG_PARAM_CHECK_VALID(host_ls_low_power_phy_clk,
"host_ls_low_power_phy_clk",
((dwc_otg_module_params.host_ls_low_power_phy_clk == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1),
((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ : DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ));
DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ddr);
DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ext_vbus);
DWC_OTG_PARAM_SET_DEFAULT(phy_utmi_width);
DWC_OTG_PARAM_SET_DEFAULT(ulpi_fs_ls);
DWC_OTG_PARAM_SET_DEFAULT(ts_dline);
#ifdef NO_FS_PHY_HW_CHECKS
retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable, "i2c_enable", 1, 0);
#else
retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable,
"i2c_enable",
(dwc_otg_module_params.i2c_enable == 1) && (core_if->hwcfg3.b.i2c == 0) ? 0 : 1,
0);
#endif
for (i = 0; i < 15; i++) {
int changed = 1;
int error = 0;
if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] == -1) {
changed = 0;
dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default;
}
if (!(dwc_otg_module_params.dev_perio_tx_fifo_size[i] <= (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) {
if (changed) {
DWC_ERROR("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", dwc_otg_module_params.dev_perio_tx_fifo_size[i], i);
error = 1;
}
dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]);
}
retval += error;
}
retval += DWC_OTG_PARAM_CHECK_VALID(en_multiple_tx_fifo, "en_multiple_tx_fifo",
((dwc_otg_module_params.en_multiple_tx_fifo == 1) && (core_if->hwcfg4.b.ded_fifo_en == 0)) ? 0 : 1,
0);
for (i = 0; i < 15; i++) {
int changed = 1;
int error = 0;
if (dwc_otg_module_params.dev_tx_fifo_size[i] == -1) {
changed = 0;
dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default;
}
if (!(dwc_otg_module_params.dev_tx_fifo_size[i] <= (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) {
if (changed) {
DWC_ERROR("%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", dwc_otg_module_params.dev_tx_fifo_size[i], i);
error = 1;
}
dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]);
}
retval += error;
}
retval += DWC_OTG_PARAM_CHECK_VALID(thr_ctl, "thr_ctl",
((dwc_otg_module_params.thr_ctl != 0) && ((dwc_otg_module_params.dma_enable == 0) || (core_if->hwcfg4.b.ded_fifo_en == 0))) ? 0 : 1,
0);
DWC_OTG_PARAM_SET_DEFAULT(tx_thr_length);
DWC_OTG_PARAM_SET_DEFAULT(rx_thr_length);
retval += DWC_OTG_PARAM_CHECK_VALID(pti_enable, "pti_enable",
((dwc_otg_module_params.pti_enable == 0) || ((dwc_otg_module_params.pti_enable == 1) && (core_if->snpsid >= 0x4F54272A))) ? 1 : 0,
0);
retval += DWC_OTG_PARAM_CHECK_VALID(mpi_enable, "mpi_enable",
((dwc_otg_module_params.mpi_enable == 0) || ((dwc_otg_module_params.mpi_enable == 1) && (core_if->hwcfg2.b.multi_proc_int == 1))) ? 1 : 0,
0);
return retval;
}
/**
* This function is the top level interrupt handler for the Common
* (Device and host modes) interrupts.
*/
static irqreturn_t dwc_otg_common_irq(int irq, void *dev)
{
dwc_otg_device_t *otg_dev = dev;
int32_t retval = IRQ_NONE;
retval = dwc_otg_handle_common_intr(otg_dev->core_if);
return IRQ_RETVAL(retval);
}
/**
* This function is called when a lm_device is unregistered with the
* dwc_otg_driver. This happens, for example, when the rmmod command is
* executed. The device may or may not be electrically present. If it is
* present, the driver stops device processing. Any resources used on behalf
* of this device are freed.
*
* @param[in] lmdev
*/
static int dwc_otg_driver_cleanup(struct platform_device *pdev)
{
dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev);
DWC_DEBUGPL(DBG_ANY, "%s(%p)\n", __func__, pdev);
if (!otg_dev) {
/* Memory allocation for the dwc_otg_device failed. */
DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__);
return 0;
}
/*
* Free the IRQ
*/
if (otg_dev->common_irq_installed) {
free_irq(otg_dev->irq, otg_dev);
}
#ifndef DWC_DEVICE_ONLY
if (otg_dev->hcd) {
dwc_otg_hcd_remove(pdev);
} else {
DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__);
return 0;
}
#endif
#ifndef DWC_HOST_ONLY
if (otg_dev->pcd) {
dwc_otg_pcd_remove(pdev);
}
#endif
if (otg_dev->core_if) {
dwc_otg_cil_remove(otg_dev->core_if);
}
/*
* Remove the device attributes
*/
dwc_otg_attr_remove(pdev);
/*
* Return the memory.
*/
if (otg_dev->base) {
iounmap(otg_dev->base);
}
kfree(otg_dev);
/*
* Clear the drvdata pointer.
*/
platform_set_drvdata(pdev, 0);
return 0;
}
/**
* This function is called when an lm_device is bound to a
* dwc_otg_driver. It creates the driver components required to
* control the device (CIL, HCD, and PCD) and it initializes the
* device. The driver components are stored in a dwc_otg_device
* structure. A reference to the dwc_otg_device is saved in the
* lm_device. This allows the driver to access the dwc_otg_device
* structure on subsequent calls to driver methods for this device.
*
* @param[in] lmdev lm_device definition
*/
static int __devinit dwc_otg_driver_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int retval = 0;
uint32_t snpsid;
dwc_otg_device_t *dwc_otg_device;
struct resource *res;
dev_dbg(dev, "dwc_otg_driver_probe(%p)\n", pdev);
dwc_otg_device = kmalloc(sizeof(dwc_otg_device_t), GFP_KERNEL);
if (!dwc_otg_device) {
dev_err(dev, "kmalloc of dwc_otg_device failed\n");
retval = -ENOMEM;
goto fail;
}
memset(dwc_otg_device, 0, sizeof(*dwc_otg_device));
dwc_otg_device->reg_offset = 0xFFFFFFFF;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "Found OTG with no register addr.\n");
retval = -ENODEV;
goto fail;
}
dwc_otg_device->rsrc_start = res->start;
dwc_otg_device->rsrc_len = res->end - res->start + 1;
dwc_otg_device->base = ioremap(dwc_otg_device->rsrc_start, dwc_otg_device->rsrc_len);
if (!dwc_otg_device->base) {
dev_err(dev, "ioremap() failed\n");
retval = -ENOMEM;
goto fail;
}
dev_dbg(dev, "base=0x%08x\n", (unsigned)dwc_otg_device->base);
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the SNPSID register contents. The value should be
* 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX".
*/
snpsid = dwc_read_reg32((uint32_t *)((uint8_t *)dwc_otg_device->base + 0x40));
if ((snpsid & 0xFFFFF000) != OTG_CORE_REV_2_00) {
dev_err(dev, "Bad value for SNPSID: 0x%08x\n", snpsid);
retval = -EINVAL;
goto fail;
}
DWC_PRINT("Core Release: %x.%x%x%x\n",
(snpsid >> 12 & 0xF),
(snpsid >> 8 & 0xF),
(snpsid >> 4 & 0xF),
(snpsid & 0xF));
/*
* Initialize driver data to point to the global DWC_otg
* Device structure.
*/
platform_set_drvdata(pdev, dwc_otg_device);
dev_dbg(dev, "dwc_otg_device=0x%p\n", dwc_otg_device);
dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->base,
&dwc_otg_module_params);
dwc_otg_device->core_if->snpsid = snpsid;
if (!dwc_otg_device->core_if) {
dev_err(dev, "CIL initialization failed!\n");
retval = -ENOMEM;
goto fail;
}
/*
* Validate parameter values.
*/
if (check_parameters(dwc_otg_device->core_if)) {
retval = -EINVAL;
goto fail;
}
/*
* Create Device Attributes in sysfs
*/
dwc_otg_attr_create(pdev);
/*
* Disable the global interrupt until all the interrupt
* handlers are installed.
*/
dwc_otg_disable_global_interrupts(dwc_otg_device->core_if);
/*
* Install the interrupt handler for the common interrupts before
* enabling common interrupts in core_init below.
*/
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "Fount OTG with to IRQ.\n");
retval = -ENODEV;
goto fail;
}
dwc_otg_device->irq = res->start;
retval = request_irq(res->start, dwc_otg_common_irq,
IRQF_SHARED, "dwc_otg", dwc_otg_device);
if (retval) {
DWC_ERROR("request of irq%d failed\n", res->start);
retval = -EBUSY;
goto fail;
} else {
dwc_otg_device->common_irq_installed = 1;
}
/*
* Initialize the DWC_otg core.
*/
dwc_otg_core_init(dwc_otg_device->core_if);
#ifndef DWC_HOST_ONLY
/*
* Initialize the PCD
*/
retval = dwc_otg_pcd_init(pdev);
if (retval != 0) {
DWC_ERROR("dwc_otg_pcd_init failed\n");
dwc_otg_device->pcd = NULL;
goto fail;
}
#endif
#ifndef DWC_DEVICE_ONLY
/*
* Initialize the HCD
*/
retval = dwc_otg_hcd_init(pdev);
if (retval != 0) {
DWC_ERROR("dwc_otg_hcd_init failed\n");
dwc_otg_device->hcd = NULL;
goto fail;
}
#endif
/*
* Enable the global interrupt after all the interrupt
* handlers are installed.
*/
dwc_otg_enable_global_interrupts(dwc_otg_device->core_if);
return 0;
fail:
dwc_otg_driver_cleanup(pdev);
return retval;
}
static int __devexit dwc_otg_driver_remove(struct platform_device *pdev)
{
return dwc_otg_driver_cleanup(pdev);
}
static struct platform_driver dwc_otg_platform_driver = {
.driver.name = "dwc_otg",
.probe = dwc_otg_driver_probe,
.remove = __devexit_p(dwc_otg_driver_remove),
};
static int __init dwc_otg_init_module(void)
{
return platform_driver_register(&dwc_otg_platform_driver);
}
static void __exit dwc_otg_cleanup_module(void)
{
platform_driver_unregister(&dwc_otg_platform_driver);
}
module_init(dwc_otg_init_module);
module_exit(dwc_otg_cleanup_module);
/**
* This function is called when the driver is removed from the kernel
* with the rmmod command. The driver unregisters itself with its bus
* driver.
*
*/
MODULE_DESCRIPTION(DWC_DRIVER_DESC);
MODULE_AUTHOR("Synopsys Inc.");
MODULE_LICENSE("GPL");
module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444);
MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None");
module_param_named(opt, dwc_otg_module_params.opt, int, 0444);
MODULE_PARM_DESC(opt, "OPT Mode");
module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444);
MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled");
module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int, 0444);
MODULE_PARM_DESC(dma_desc_enable, "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled");
module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int, 0444);
MODULE_PARM_DESC(dma_burst_size, "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256");
module_param_named(speed, dwc_otg_module_params.speed, int, 0444);
MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed");
module_param_named(host_support_fs_ls_low_power, dwc_otg_module_params.host_support_fs_ls_low_power, int, 0444);
MODULE_PARM_DESC(host_support_fs_ls_low_power, "Support Low Power w/FS or LS 0=Support 1=Don't Support");
module_param_named(host_ls_low_power_phy_clk, dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444);
MODULE_PARM_DESC(host_ls_low_power_phy_clk, "Low Speed Low Power Clock 0=48Mhz 1=6Mhz");
module_param_named(enable_dynamic_fifo, dwc_otg_module_params.enable_dynamic_fifo, int, 0444);
MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing");
module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int, 0444);
MODULE_PARM_DESC(data_fifo_size, "Total number of words in the data FIFO memory 32-32768");
module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size, int, 0444);
MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
module_param_named(dev_nperio_tx_fifo_size, dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444);
MODULE_PARM_DESC(dev_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768");
module_param_named(dev_perio_tx_fifo_size_1, dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_1, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_2, dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_2, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_3, dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_3, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_4, dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_4, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_5, dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_5, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_6, dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_6, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_7, dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_7, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_8, dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_8, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_9, dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_9, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_10, dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_10, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_11, dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_11, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_12, dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_12, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_13, dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_13, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_14, dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_14, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(dev_perio_tx_fifo_size_15, dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444);
MODULE_PARM_DESC(dev_perio_tx_fifo_size_15, "Number of words in the periodic Tx FIFO 4-768");
module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size, int, 0444);
MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768");
module_param_named(host_nperio_tx_fifo_size, dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444);
MODULE_PARM_DESC(host_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768");
module_param_named(host_perio_tx_fifo_size, dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444);
MODULE_PARM_DESC(host_perio_tx_fifo_size, "Number of words in the host periodic Tx FIFO 16-32768");
module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size, int, 0444);
/** @todo Set the max to 512K, modify checks */
MODULE_PARM_DESC(max_transfer_size, "The maximum transfer size supported in bytes 2047-65535");
module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count, int, 0444);
MODULE_PARM_DESC(max_packet_count, "The maximum number of packets in a transfer 15-511");
module_param_named(host_channels, dwc_otg_module_params.host_channels, int, 0444);
MODULE_PARM_DESC(host_channels, "The number of host channel registers to use 1-16");
module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int, 0444);
MODULE_PARM_DESC(dev_endpoints, "The number of endpoints in addition to EP0 available for device mode 1-15");
module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444);
MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI");
module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int, 0444);
MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits");
module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444);
MODULE_PARM_DESC(phy_ulpi_ddr, "ULPI at double or single data rate 0=Single 1=Double");
module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus, int, 0444);
MODULE_PARM_DESC(phy_ulpi_ext_vbus, "ULPI PHY using internal or external vbus 0=Internal");
module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444);
MODULE_PARM_DESC(i2c_enable, "FS PHY Interface");
module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444);
MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only");
module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444);
MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs");
module_param_named(debug, g_dbg_lvl, int, 0444);
MODULE_PARM_DESC(debug, "");
module_param_named(en_multiple_tx_fifo, dwc_otg_module_params.en_multiple_tx_fifo, int, 0444);
MODULE_PARM_DESC(en_multiple_tx_fifo, "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled");
module_param_named(dev_tx_fifo_size_1, dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_2, dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_3, dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_4, dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_5, dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_6, dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_7, dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_8, dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_9, dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_10, dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_11, dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_12, dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_13, dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_14, dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768");
module_param_named(dev_tx_fifo_size_15, dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444);
MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768");
module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444);
MODULE_PARM_DESC(thr_ctl, "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled");
module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int, 0444);
MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs");
module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int, 0444);
MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs");
module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444);
MODULE_PARM_DESC(pti_enable, "Per Transfer Interrupt mode 0=disabled 1=enabled");
module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444);
MODULE_PARM_DESC(mpi_enable, "Multiprocessor Interrupt mode 0=disabled 1=enabled");

View File

@ -0,0 +1,62 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $
* $Revision: #12 $
* $Date: 2008/07/15 $
* $Change: 1064918 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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 __DWC_OTG_DRIVER_H__
#define __DWC_OTG_DRIVER_H__
/** @file
* This file contains the interface to the Linux driver.
*/
#include "otg_cil.h"
/* Type declarations */
struct dwc_otg_pcd;
struct dwc_otg_hcd;
/**
* This structure is a wrapper that encapsulates the driver components used to
* manage a single DWC_otg controller.
*/
typedef struct dwc_otg_device {
void *base;
dwc_otg_core_if_t *core_if;
uint32_t reg_offset;
struct dwc_otg_pcd *pcd;
struct dwc_otg_hcd *hcd;
uint8_t common_irq_installed;
int irq;
uint32_t rsrc_start;
uint32_t rsrc_len;
} dwc_otg_device_t;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,652 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $
* $Revision: #45 $
* $Date: 2008/07/15 $
* $Change: 1064918 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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 DWC_DEVICE_ONLY
#ifndef __DWC_HCD_H__
#define __DWC_HCD_H__
#include <linux/list.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
struct dwc_otg_device;
#include "otg_cil.h"
/**
* @file
*
* This file contains the structures, constants, and interfaces for
* the Host Contoller Driver (HCD).
*
* The Host Controller Driver (HCD) is responsible for translating requests
* from the USB Driver into the appropriate actions on the DWC_otg controller.
* It isolates the USBD from the specifics of the controller by providing an
* API to the USBD.
*/
/**
* Phases for control transfers.
*/
typedef enum dwc_otg_control_phase {
DWC_OTG_CONTROL_SETUP,
DWC_OTG_CONTROL_DATA,
DWC_OTG_CONTROL_STATUS
} dwc_otg_control_phase_e;
/** Transaction types. */
typedef enum dwc_otg_transaction_type {
DWC_OTG_TRANSACTION_NONE,
DWC_OTG_TRANSACTION_PERIODIC,
DWC_OTG_TRANSACTION_NON_PERIODIC,
DWC_OTG_TRANSACTION_ALL
} dwc_otg_transaction_type_e;
/**
* A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
* interrupt, or isochronous transfer. A single QTD is created for each URB
* (of one of these types) submitted to the HCD. The transfer associated with
* a QTD may require one or multiple transactions.
*
* A QTD is linked to a Queue Head, which is entered in either the
* non-periodic or periodic schedule for execution. When a QTD is chosen for
* execution, some or all of its transactions may be executed. After
* execution, the state of the QTD is updated. The QTD may be retired if all
* its transactions are complete or if an error occurred. Otherwise, it
* remains in the schedule so more transactions can be executed later.
*/
typedef struct dwc_otg_qtd {
/**
* Determines the PID of the next data packet for the data phase of
* control transfers. Ignored for other transfer types.<br>
* One of the following values:
* - DWC_OTG_HC_PID_DATA0
* - DWC_OTG_HC_PID_DATA1
*/
uint8_t data_toggle;
/** Current phase for control transfers (Setup, Data, or Status). */
dwc_otg_control_phase_e control_phase;
/** Keep track of the current split type
* for FS/LS endpoints on a HS Hub */
uint8_t complete_split;
/** How many bytes transferred during SSPLIT OUT */
uint32_t ssplit_out_xfer_count;
/**
* Holds the number of bus errors that have occurred for a transaction
* within this transfer.
*/
uint8_t error_count;
/**
* Index of the next frame descriptor for an isochronous transfer. A
* frame descriptor describes the buffer position and length of the
* data to be transferred in the next scheduled (micro)frame of an
* isochronous transfer. It also holds status for that transaction.
* The frame index starts at 0.
*/
int isoc_frame_index;
/** Position of the ISOC split on full/low speed */
uint8_t isoc_split_pos;
/** Position of the ISOC split in the buffer for the current frame */
uint16_t isoc_split_offset;
/** URB for this transfer */
struct urb *urb;
/** This list of QTDs */
struct list_head qtd_list_entry;
} dwc_otg_qtd_t;
/**
* A Queue Head (QH) holds the static characteristics of an endpoint and
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
* be entered in either the non-periodic or periodic schedule.
*/
typedef struct dwc_otg_qh {
/**
* Endpoint type.
* One of the following values:
* - USB_ENDPOINT_XFER_CONTROL
* - USB_ENDPOINT_XFER_ISOC
* - USB_ENDPOINT_XFER_BULK
* - USB_ENDPOINT_XFER_INT
*/
uint8_t ep_type;
uint8_t ep_is_in;
/** wMaxPacketSize Field of Endpoint Descriptor. */
uint16_t maxp;
/**
* Determines the PID of the next data packet for non-control
* transfers. Ignored for control transfers.<br>
* One of the following values:
* - DWC_OTG_HC_PID_DATA0
* - DWC_OTG_HC_PID_DATA1
*/
uint8_t data_toggle;
/** Ping state if 1. */
uint8_t ping_state;
/**
* List of QTDs for this QH.
*/
struct list_head qtd_list;
/** Host channel currently processing transfers for this QH. */
dwc_hc_t *channel;
/** QTD currently assigned to a host channel for this QH. */
dwc_otg_qtd_t *qtd_in_process;
/** Full/low speed endpoint on high-speed hub requires split. */
uint8_t do_split;
/** @name Periodic schedule information */
/** @{ */
/** Bandwidth in microseconds per (micro)frame. */
uint8_t usecs;
/** Interval between transfers in (micro)frames. */
uint16_t interval;
/**
* (micro)frame to initialize a periodic transfer. The transfer
* executes in the following (micro)frame.
*/
uint16_t sched_frame;
/** (micro)frame at which last start split was initialized. */
uint16_t start_split_frame;
u16 speed;
u16 frame_usecs[8];
/** @} */
/** Entry for QH in either the periodic or non-periodic schedule. */
struct list_head qh_list_entry;
} dwc_otg_qh_t;
/**
* This structure holds the state of the HCD, including the non-periodic and
* periodic schedules.
*/
typedef struct dwc_otg_hcd {
/** The DWC otg device pointer */
struct dwc_otg_device *otg_dev;
/** DWC OTG Core Interface Layer */
dwc_otg_core_if_t *core_if;
/** Internal DWC HCD Flags */
volatile union dwc_otg_hcd_internal_flags {
uint32_t d32;
struct {
unsigned port_connect_status_change : 1;
unsigned port_connect_status : 1;
unsigned port_reset_change : 1;
unsigned port_enable_change : 1;
unsigned port_suspend_change : 1;
unsigned port_over_current_change : 1;
unsigned reserved : 27;
} b;
} flags;
/**
* Inactive items in the non-periodic schedule. This is a list of
* Queue Heads. Transfers associated with these Queue Heads are not
* currently assigned to a host channel.
*/
struct list_head non_periodic_sched_inactive;
/**
* Active items in the non-periodic schedule. This is a list of
* Queue Heads. Transfers associated with these Queue Heads are
* currently assigned to a host channel.
*/
struct list_head non_periodic_sched_active;
/**
* Pointer to the next Queue Head to process in the active
* non-periodic schedule.
*/
struct list_head *non_periodic_qh_ptr;
/**
* Inactive items in the periodic schedule. This is a list of QHs for
* periodic transfers that are _not_ scheduled for the next frame.
* Each QH in the list has an interval counter that determines when it
* needs to be scheduled for execution. This scheduling mechanism
* allows only a simple calculation for periodic bandwidth used (i.e.
* must assume that all periodic transfers may need to execute in the
* same frame). However, it greatly simplifies scheduling and should
* be sufficient for the vast majority of OTG hosts, which need to
* connect to a small number of peripherals at one time.
*
* Items move from this list to periodic_sched_ready when the QH
* interval counter is 0 at SOF.
*/
struct list_head periodic_sched_inactive;
/**
* List of periodic QHs that are ready for execution in the next
* frame, but have not yet been assigned to host channels.
*
* Items move from this list to periodic_sched_assigned as host
* channels become available during the current frame.
*/
struct list_head periodic_sched_ready;
/**
* List of periodic QHs to be executed in the next frame that are
* assigned to host channels.
*
* Items move from this list to periodic_sched_queued as the
* transactions for the QH are queued to the DWC_otg controller.
*/
struct list_head periodic_sched_assigned;
/**
* List of periodic QHs that have been queued for execution.
*
* Items move from this list to either periodic_sched_inactive or
* periodic_sched_ready when the channel associated with the transfer
* is released. If the interval for the QH is 1, the item moves to
* periodic_sched_ready because it must be rescheduled for the next
* frame. Otherwise, the item moves to periodic_sched_inactive.
*/
struct list_head periodic_sched_queued;
/**
* Total bandwidth claimed so far for periodic transfers. This value
* is in microseconds per (micro)frame. The assumption is that all
* periodic transfers may occur in the same (micro)frame.
*/
uint16_t periodic_usecs;
/*
* Total bandwidth claimed so far for all periodic transfers
* in a frame.
* This will include a mixture of HS and FS transfers.
* Units are microseconds per (micro)frame.
* We have a budget per frame and have to schedule
* transactions accordingly.
* Watch out for the fact that things are actually scheduled for the
* "next frame".
*/
u16 frame_usecs[8];
/**
* Frame number read from the core at SOF. The value ranges from 0 to
* DWC_HFNUM_MAX_FRNUM.
*/
uint16_t frame_number;
/**
* Free host channels in the controller. This is a list of
* dwc_hc_t items.
*/
struct list_head free_hc_list;
/**
* Number of host channels assigned to periodic transfers. Currently
* assuming that there is a dedicated host channel for each periodic
* transaction and at least one host channel available for
* non-periodic transactions.
*/
int periodic_channels;
/**
* Number of host channels assigned to non-periodic transfers.
*/
int non_periodic_channels;
/**
* Array of pointers to the host channel descriptors. Allows accessing
* a host channel descriptor given the host channel number. This is
* useful in interrupt handlers.
*/
dwc_hc_t *hc_ptr_array[MAX_EPS_CHANNELS];
/**
* Buffer to use for any data received during the status phase of a
* control transfer. Normally no data is transferred during the status
* phase. This buffer is used as a bit bucket.
*/
uint8_t *status_buf;
/**
* DMA address for status_buf.
*/
dma_addr_t status_buf_dma;
#define DWC_OTG_HCD_STATUS_BUF_SIZE 64
/**
* Structure to allow starting the HCD in a non-interrupt context
* during an OTG role change.
*/
struct delayed_work start_work;
/**
* Connection timer. An OTG host must display a message if the device
* does not connect. Started when the VBus power is turned on via
* sysfs attribute "buspower".
*/
struct timer_list conn_timer;
/* Tasket to do a reset */
struct tasklet_struct *reset_tasklet;
/* */
spinlock_t lock;
#ifdef DEBUG
uint32_t frrem_samples;
uint64_t frrem_accum;
uint32_t hfnum_7_samples_a;
uint64_t hfnum_7_frrem_accum_a;
uint32_t hfnum_0_samples_a;
uint64_t hfnum_0_frrem_accum_a;
uint32_t hfnum_other_samples_a;
uint64_t hfnum_other_frrem_accum_a;
uint32_t hfnum_7_samples_b;
uint64_t hfnum_7_frrem_accum_b;
uint32_t hfnum_0_samples_b;
uint64_t hfnum_0_frrem_accum_b;
uint32_t hfnum_other_samples_b;
uint64_t hfnum_other_frrem_accum_b;
#endif
} dwc_otg_hcd_t;
/** Gets the dwc_otg_hcd from a struct usb_hcd */
static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
{
return (dwc_otg_hcd_t *)(hcd->hcd_priv);
}
/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */
static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t *dwc_otg_hcd)
{
return container_of((void *)dwc_otg_hcd, struct usb_hcd, hcd_priv);
}
/** @name HCD Create/Destroy Functions */
/** @{ */
extern int dwc_otg_hcd_init(struct platform_device *pdev);
extern void dwc_otg_hcd_remove(struct platform_device *pdev);
/** @} */
/** @name Linux HC Driver API Functions */
/** @{ */
extern int dwc_otg_hcd_start(struct usb_hcd *hcd);
extern void dwc_otg_hcd_stop(struct usb_hcd *hcd);
extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
extern void dwc_otg_hcd_free(struct usb_hcd *hcd);
extern int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd,
// struct usb_host_endpoint *ep,
struct urb *urb,
gfp_t mem_flags
);
extern int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd,
struct urb *urb, int status);
extern void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
extern irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd);
extern int dwc_otg_hcd_hub_status_data(struct usb_hcd *hcd,
char *buf);
extern int dwc_otg_hcd_hub_control(struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength);
/** @} */
/** @name Transaction Execution Functions */
/** @{ */
extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd);
extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *hcd,
dwc_otg_transaction_type_e tr_type);
extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *urb,
int status);
/** @} */
/** @name Interrupt Handler Functions */
/** @{ */
extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t *dwc_otg_hcd, uint32_t num);
extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t *dwc_otg_hcd);
extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *dwc_otg_hcd);
/** @} */
/** @name Schedule Queue Functions */
/** @{ */
/* Implemented in dwc_otg_hcd_queue.c */
extern int init_hcd_usecs(dwc_otg_hcd_t *hcd);
extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t *hcd, struct urb *urb);
extern void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb);
extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh);
extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_csplit);
/** Remove and free a QH */
static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t *hcd,
dwc_otg_qh_t *qh)
{
dwc_otg_hcd_qh_remove(hcd, qh);
dwc_otg_hcd_qh_free(hcd, qh);
}
/** Allocates memory for a QH structure.
* @return Returns the memory allocate or NULL on error. */
static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(void)
{
return (dwc_otg_qh_t *) kmalloc(sizeof(dwc_otg_qh_t), GFP_KERNEL);
}
extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(struct urb *urb);
extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t *qtd, struct urb *urb);
extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *qtd, dwc_otg_hcd_t *dwc_otg_hcd);
/** Allocates memory for a QTD structure.
* @return Returns the memory allocate or NULL on error. */
static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(void)
{
return (dwc_otg_qtd_t *) kmalloc(sizeof(dwc_otg_qtd_t), GFP_KERNEL);
}
/** Frees the memory for a QTD structure. QTD should already be removed from
* list.
* @param[in] qtd QTD to free.*/
static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t *qtd)
{
kfree(qtd);
}
/** Remove and free a QTD */
static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t *hcd, dwc_otg_qtd_t *qtd)
{
list_del(&qtd->qtd_list_entry);
dwc_otg_hcd_qtd_free(qtd);
}
/** @} */
/** @name Internal Functions */
/** @{ */
dwc_otg_qh_t *dwc_urb_to_qh(struct urb *urb);
void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *hcd);
void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *hcd);
/** @} */
/** Gets the usb_host_endpoint associated with an URB. */
static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb)
{
struct usb_device *dev = urb->dev;
int ep_num = usb_pipeendpoint(urb->pipe);
if (usb_pipein(urb->pipe))
return dev->ep_in[ep_num];
else
return dev->ep_out[ep_num];
}
/**
* Gets the endpoint number from a _bEndpointAddress argument. The endpoint is
* qualified with its direction (possible 32 endpoints per device).
*/
#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
/** Gets the QH that contains the list_head */
#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry)
/** Gets the QTD that contains the list_head */
#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry)
/** Check if QH is non-periodic */
#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
(_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))
/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */
#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
/** Packet size for any kind of endpoint descriptor */
#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
/**
* Returns true if _frame1 is less than or equal to _frame2. The comparison is
* done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
* frame number when the max frame number is reached.
*/
static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2)
{
return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <=
(DWC_HFNUM_MAX_FRNUM >> 1);
}
/**
* Returns true if _frame1 is greater than _frame2. The comparison is done
* modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
* number when the max frame number is reached.
*/
static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2)
{
return (frame1 != frame2) &&
(((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) <
(DWC_HFNUM_MAX_FRNUM >> 1));
}
/**
* Increments _frame by the amount specified by _inc. The addition is done
* modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
*/
static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc)
{
return (frame + inc) & DWC_HFNUM_MAX_FRNUM;
}
static inline uint16_t dwc_full_frame_num(uint16_t frame)
{
return (frame & DWC_HFNUM_MAX_FRNUM) >> 3;
}
static inline uint16_t dwc_micro_frame_num(uint16_t frame)
{
return frame & 0x7;
}
#ifdef DEBUG
/**
* Macro to sample the remaining PHY clocks left in the current frame. This
* may be used during debugging to determine the average time it takes to
* execute sections of code. There are two possible sample points, "a" and
* "b", so the _letter argument must be one of these values.
*
* To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
* example, "cat /sys/devices/lm0/hcd_frrem".
*/
#define dwc_sample_frrem(_hcd, _qh, _letter) \
{ \
hfnum_data_t hfnum; \
dwc_otg_qtd_t *qtd; \
qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \
if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \
hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); \
switch (hfnum.b.frnum & 0x7) { \
case 7: \
_hcd->hfnum_7_samples_##_letter++; \
_hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \
break; \
case 0: \
_hcd->hfnum_0_samples_##_letter++; \
_hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \
break; \
default: \
_hcd->hfnum_other_samples_##_letter++; \
_hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \
break; \
} \
} \
}
#else
#define dwc_sample_frrem(_hcd, _qh, _letter)
#endif
#endif
#endif /* DWC_DEVICE_ONLY */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,794 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $
* $Revision: #33 $
* $Date: 2008/07/15 $
* $Change: 1064918 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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 DWC_DEVICE_ONLY
/**
* @file
*
* This file contains the functions to manage Queue Heads and Queue
* Transfer Descriptors.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/version.h>
#include <mach/irqs.h>
#include "otg_driver.h"
#include "otg_hcd.h"
#include "otg_regs.h"
/**
* This function allocates and initializes a QH.
*
* @param hcd The HCD state structure for the DWC OTG controller.
* @param[in] urb Holds the information about the device/endpoint that we need
* to initialize the QH.
*
* @return Returns pointer to the newly allocated QH, or NULL on error. */
dwc_otg_qh_t *dwc_otg_hcd_qh_create (dwc_otg_hcd_t *hcd, struct urb *urb)
{
dwc_otg_qh_t *qh;
/* Allocate memory */
/** @todo add memflags argument */
qh = dwc_otg_hcd_qh_alloc ();
if (qh == NULL) {
return NULL;
}
dwc_otg_hcd_qh_init (hcd, qh, urb);
return qh;
}
/** Free each QTD in the QH's QTD-list then free the QH. QH should already be
* removed from a list. QTD list should already be empty if called from URB
* Dequeue.
*
* @param[in] hcd HCD instance.
* @param[in] qh The QH to free.
*/
void dwc_otg_hcd_qh_free (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
dwc_otg_qtd_t *qtd;
struct list_head *pos;
//unsigned long flags;
/* Free each QTD in the QTD list */
#ifdef CONFIG_SMP
//the spinlock is locked before this function get called,
//but in case the lock is needed, the check function is preserved
//but in non-SMP mode, all spinlock is lockable.
//don't do the test in non-SMP mode
if(spin_trylock(&hcd->lock)) {
printk("%s: It is not supposed to be lockable!!\n",__func__);
BUG();
}
#endif
// SPIN_LOCK_IRQSAVE(&hcd->lock, flags)
for (pos = qh->qtd_list.next;
pos != &qh->qtd_list;
pos = qh->qtd_list.next)
{
list_del (pos);
qtd = dwc_list_to_qtd (pos);
dwc_otg_hcd_qtd_free (qtd);
}
// SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags)
kfree (qh);
return;
}
/** Initializes a QH structure.
*
* @param[in] hcd The HCD state structure for the DWC OTG controller.
* @param[in] qh The QH to init.
* @param[in] urb Holds the information about the device/endpoint that we need
* to initialize the QH. */
#define SCHEDULE_SLOP 10
void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb)
{
char *speed, *type;
memset (qh, 0, sizeof (dwc_otg_qh_t));
/* Initialize QH */
switch (usb_pipetype(urb->pipe)) {
case PIPE_CONTROL:
qh->ep_type = USB_ENDPOINT_XFER_CONTROL;
break;
case PIPE_BULK:
qh->ep_type = USB_ENDPOINT_XFER_BULK;
break;
case PIPE_ISOCHRONOUS:
qh->ep_type = USB_ENDPOINT_XFER_ISOC;
break;
case PIPE_INTERRUPT:
qh->ep_type = USB_ENDPOINT_XFER_INT;
break;
}
qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0;
qh->data_toggle = DWC_OTG_HC_PID_DATA0;
qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe)));
INIT_LIST_HEAD(&qh->qtd_list);
INIT_LIST_HEAD(&qh->qh_list_entry);
qh->channel = NULL;
qh->speed = urb->dev->speed;
/* FS/LS Enpoint on HS Hub
* NOT virtual root hub */
qh->do_split = 0;
if (((urb->dev->speed == USB_SPEED_LOW) ||
(urb->dev->speed == USB_SPEED_FULL)) &&
(urb->dev->tt) && (urb->dev->tt->hub) && (urb->dev->tt->hub->devnum != 1))
{
DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n",
usb_pipeendpoint(urb->pipe), urb->dev->tt->hub->devnum,
urb->dev->ttport);
qh->do_split = 1;
}
if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
/* Compute scheduling parameters once and save them. */
hprt0_data_t hprt;
/** @todo Account for split transfers in the bus time. */
int bytecount = dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed,
usb_pipein(urb->pipe),
(qh->ep_type == USB_ENDPOINT_XFER_ISOC),
bytecount));
/* Start in a slightly future (micro)frame. */
qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
SCHEDULE_SLOP);
qh->interval = urb->interval;
#if 0
/* Increase interrupt polling rate for debugging. */
if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
qh->interval = 8;
}
#endif
hprt.d32 = dwc_read_reg32(hcd->core_if->host_if->hprt0);
if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) &&
((urb->dev->speed == USB_SPEED_LOW) ||
(urb->dev->speed == USB_SPEED_FULL))) {
qh->interval *= 8;
qh->sched_frame |= 0x7;
qh->start_split_frame = qh->sched_frame;
}
}
DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh);
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
urb->dev->devnum);
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) == USB_DIR_IN ? "IN" : "OUT");
switch(urb->dev->speed) {
case USB_SPEED_LOW:
speed = "low";
break;
case USB_SPEED_FULL:
speed = "full";
break;
case USB_SPEED_HIGH:
speed = "high";
break;
default:
speed = "?";
break;
}
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed);
switch (qh->ep_type) {
case USB_ENDPOINT_XFER_ISOC:
type = "isochronous";
break;
case USB_ENDPOINT_XFER_INT:
type = "interrupt";
break;
case USB_ENDPOINT_XFER_CONTROL:
type = "control";
break;
case USB_ENDPOINT_XFER_BULK:
type = "bulk";
break;
default:
type = "?";
break;
}
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n",type);
#ifdef DEBUG
if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
qh->usecs);
DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
qh->interval);
}
#endif
return;
}
/**
* Microframe scheduler
* track the total use in hcd->frame_usecs
* keep each qh use in qh->frame_usecs
* when surrendering the qh then donate the time back
*/
static const u16 max_uframe_usecs[] = { 100, 100, 100, 100, 100, 100, 30, 0 };
/*
* called from dwc_otg_hcd.c:dwc_otg_hcd_init
*/
int init_hcd_usecs(dwc_otg_hcd_t *hcd)
{
int i;
for (i = 0; i < 8; i++)
hcd->frame_usecs[i] = max_uframe_usecs[i];
return 0;
}
static int find_single_uframe(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
int i;
u16 utime;
int t_left;
int ret;
int done;
ret = -1;
utime = qh->usecs;
t_left = utime;
i = 0;
done = 0;
while (done == 0) {
/* At the start hcd->frame_usecs[i] = max_uframe_usecs[i]; */
if (utime <= hcd->frame_usecs[i]) {
hcd->frame_usecs[i] -= utime;
qh->frame_usecs[i] += utime;
t_left -= utime;
ret = i;
done = 1;
return ret;
} else {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
}
}
return ret;
}
/*
* use this for FS apps that can span multiple uframes
*/
static int find_multi_uframe(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
int i;
int j;
u16 utime;
int t_left;
int ret;
int done;
u16 xtime;
ret = -1;
utime = qh->usecs;
t_left = utime;
i = 0;
done = 0;
loop:
while (done == 0) {
if (hcd->frame_usecs[i] <= 0) {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
goto loop;
}
/*
* We need n consequtive slots so use j as a start slot.
* j plus j+1 must be enough time (for now)
*/
xtime = hcd->frame_usecs[i];
for (j = i + 1; j < 8; j++) {
/*
* if we add this frame remaining time to xtime we may
* be OK, if not we need to test j for a complete frame.
*/
if ((xtime + hcd->frame_usecs[j]) < utime) {
if (hcd->frame_usecs[j] < max_uframe_usecs[j]) {
j = 8;
ret = -1;
continue;
}
}
if (xtime >= utime) {
ret = i;
j = 8; /* stop loop with a good value ret */
continue;
}
/* add the frame time to x time */
xtime += hcd->frame_usecs[j];
/* we must have a fully available next frame or break */
if ((xtime < utime) &&
(hcd->frame_usecs[j] == max_uframe_usecs[j])) {
ret = -1;
j = 8; /* stop loop with a bad value ret */
continue;
}
}
if (ret >= 0) {
t_left = utime;
for (j = i; (t_left > 0) && (j < 8); j++) {
t_left -= hcd->frame_usecs[j];
if (t_left <= 0) {
qh->frame_usecs[j] +=
hcd->frame_usecs[j] + t_left;
hcd->frame_usecs[j] = -t_left;
ret = i;
done = 1;
} else {
qh->frame_usecs[j] +=
hcd->frame_usecs[j];
hcd->frame_usecs[j] = 0;
}
}
} else {
i++;
if (i == 8) {
done = 1;
ret = -1;
}
}
}
return ret;
}
static int find_uframe(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
int ret = -1;
if (qh->speed == USB_SPEED_HIGH)
/* if this is a hs transaction we need a full frame */
ret = find_single_uframe(hcd, qh);
else
/* FS transaction may need a sequence of frames */
ret = find_multi_uframe(hcd, qh);
return ret;
}
/**
* Checks that the max transfer size allowed in a host channel is large enough
* to handle the maximum data transfer in a single (micro)frame for a periodic
* transfer.
*
* @param hcd The HCD state structure for the DWC OTG controller.
* @param qh QH for a periodic endpoint.
*
* @return 0 if successful, negative error code otherwise.
*/
static int check_max_xfer_size(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
int status;
uint32_t max_xfer_size;
uint32_t max_channel_xfer_size;
status = 0;
max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
if (max_xfer_size > max_channel_xfer_size) {
DWC_NOTICE("%s: Periodic xfer length %d > "
"max xfer length for channel %d\n",
__func__, max_xfer_size, max_channel_xfer_size);
status = -ENOSPC;
}
return status;
}
/**
* Schedules an interrupt or isochronous transfer in the periodic schedule.
*/
static int schedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
int status;
struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
int frame;
status = find_uframe(hcd, qh);
frame = -1;
if (status == 0) {
frame = 7;
} else {
if (status > 0)
frame = status - 1;
}
/* Set the new frame up */
if (frame > -1) {
qh->sched_frame &= ~0x7;
qh->sched_frame |= (frame & 7);
}
if (status != -1)
status = 0;
if (status) {
pr_notice("%s: Insufficient periodic bandwidth for "
"periodic transfer.\n", __func__);
return status;
}
status = check_max_xfer_size(hcd, qh);
if (status) {
pr_notice("%s: Channel max transfer size too small "
"for periodic transfer.\n", __func__);
return status;
}
/* Always start in the inactive schedule. */
list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive);
/* Update claimed usecs per (micro)frame. */
hcd->periodic_usecs += qh->usecs;
/*
* Update average periodic bandwidth claimed and # periodic reqs for
* usbfs.
*/
bus->bandwidth_allocated += qh->usecs / qh->interval;
if (qh->ep_type == USB_ENDPOINT_XFER_INT)
bus->bandwidth_int_reqs++;
else
bus->bandwidth_isoc_reqs++;
return status;
}
/**
* This function adds a QH to either the non periodic or periodic schedule if
* it is not already in the schedule. If the QH is already in the schedule, no
* action is taken.
*
* @return 0 if successful, negative error code otherwise.
*/
int dwc_otg_hcd_qh_add (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
//unsigned long flags;
int status = 0;
#ifdef CONFIG_SMP
//the spinlock is locked before this function get called,
//but in case the lock is needed, the check function is preserved
//but in non-SMP mode, all spinlock is lockable.
//don't do the test in non-SMP mode
if(spin_trylock(&hcd->lock)) {
printk("%s: It is not supposed to be lockable!!\n",__func__);
BUG();
}
#endif
// SPIN_LOCK_IRQSAVE(&hcd->lock, flags)
if (!list_empty(&qh->qh_list_entry)) {
/* QH already in a schedule. */
goto done;
}
/* Add the new QH to the appropriate schedule */
if (dwc_qh_is_non_per(qh)) {
/* Always start in the inactive schedule. */
list_add_tail(&qh->qh_list_entry, &hcd->non_periodic_sched_inactive);
} else {
status = schedule_periodic(hcd, qh);
}
done:
// SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags)
return status;
}
/**
* Removes an interrupt or isochronous transfer from the periodic schedule.
*/
static void deschedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
struct usb_bus *bus = hcd_to_bus(dwc_otg_hcd_to_hcd(hcd));
int i;
list_del_init(&qh->qh_list_entry);
/* Update claimed usecs per (micro)frame. */
hcd->periodic_usecs -= qh->usecs;
for (i = 0; i < 8; i++) {
hcd->frame_usecs[i] += qh->frame_usecs[i];
qh->frame_usecs[i] = 0;
}
/*
* Update average periodic bandwidth claimed and # periodic reqs for
* usbfs.
*/
bus->bandwidth_allocated -= qh->usecs / qh->interval;
if (qh->ep_type == USB_ENDPOINT_XFER_INT)
bus->bandwidth_int_reqs--;
else
bus->bandwidth_isoc_reqs--;
}
/**
* Removes a QH from either the non-periodic or periodic schedule. Memory is
* not freed.
*
* @param[in] hcd The HCD state structure.
* @param[in] qh QH to remove from schedule. */
void dwc_otg_hcd_qh_remove (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
{
//unsigned long flags;
#ifdef CONFIG_SMP
//the spinlock is locked before this function get called,
//but in case the lock is needed, the check function is preserved
//but in non-SMP mode, all spinlock is lockable.
//don't do the test in non-SMP mode
if(spin_trylock(&hcd->lock)) {
printk("%s: It is not supposed to be lockable!!\n",__func__);
BUG();
}
#endif
// SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
if (list_empty(&qh->qh_list_entry)) {
/* QH is not in a schedule. */
goto done;
}
if (dwc_qh_is_non_per(qh)) {
if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
}
list_del_init(&qh->qh_list_entry);
} else {
deschedule_periodic(hcd, qh);
}
done:
// SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
return;
}
/**
* Deactivates a QH. For non-periodic QHs, removes the QH from the active
* non-periodic schedule. The QH is added to the inactive non-periodic
* schedule if any QTDs are still attached to the QH.
*
* For periodic QHs, the QH is removed from the periodic queued schedule. If
* there are any QTDs still attached to the QH, the QH is added to either the
* periodic inactive schedule or the periodic ready schedule and its next
* scheduled frame is calculated. The QH is placed in the ready schedule if
* the scheduled frame has been reached already. Otherwise it's placed in the
* inactive schedule. If there are no QTDs attached to the QH, the QH is
* completely removed from the periodic schedule.
*/
void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_next_periodic_split)
{
unsigned long flags;
SPIN_LOCK_IRQSAVE(&hcd->lock, flags);
if (dwc_qh_is_non_per(qh)) {
dwc_otg_hcd_qh_remove(hcd, qh);
if (!list_empty(&qh->qtd_list)) {
/* Add back to inactive non-periodic schedule. */
dwc_otg_hcd_qh_add(hcd, qh);
}
} else {
uint16_t frame_number = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
if (qh->do_split) {
/* Schedule the next continuing periodic split transfer */
if (sched_next_periodic_split) {
qh->sched_frame = frame_number;
if (dwc_frame_num_le(frame_number,
dwc_frame_num_inc(qh->start_split_frame, 1))) {
/*
* Allow one frame to elapse after start
* split microframe before scheduling
* complete split, but DONT if we are
* doing the next start split in the
* same frame for an ISOC out.
*/
if ((qh->ep_type != USB_ENDPOINT_XFER_ISOC) || (qh->ep_is_in != 0)) {
qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, 1);
}
}
} else {
qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame,
qh->interval);
if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
qh->sched_frame = frame_number;
}
qh->sched_frame |= 0x7;
qh->start_split_frame = qh->sched_frame;
}
} else {
qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval);
if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
qh->sched_frame = frame_number;
}
}
if (list_empty(&qh->qtd_list)) {
dwc_otg_hcd_qh_remove(hcd, qh);
} else {
/*
* Remove from periodic_sched_queued and move to
* appropriate queue.
*/
if (qh->sched_frame == frame_number) {
list_move(&qh->qh_list_entry,
&hcd->periodic_sched_ready);
} else {
list_move(&qh->qh_list_entry,
&hcd->periodic_sched_inactive);
}
}
}
SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags);
}
/**
* This function allocates and initializes a QTD.
*
* @param[in] urb The URB to create a QTD from. Each URB-QTD pair will end up
* pointing to each other so each pair should have a unique correlation.
*
* @return Returns pointer to the newly allocated QTD, or NULL on error. */
dwc_otg_qtd_t *dwc_otg_hcd_qtd_create (struct urb *urb)
{
dwc_otg_qtd_t *qtd;
qtd = dwc_otg_hcd_qtd_alloc ();
if (qtd == NULL) {
return NULL;
}
dwc_otg_hcd_qtd_init (qtd, urb);
return qtd;
}
/**
* Initializes a QTD structure.
*
* @param[in] qtd The QTD to initialize.
* @param[in] urb The URB to use for initialization. */
void dwc_otg_hcd_qtd_init (dwc_otg_qtd_t *qtd, struct urb *urb)
{
memset (qtd, 0, sizeof (dwc_otg_qtd_t));
qtd->urb = urb;
if (usb_pipecontrol(urb->pipe)) {
/*
* The only time the QTD data toggle is used is on the data
* phase of control transfers. This phase always starts with
* DATA1.
*/
qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
qtd->control_phase = DWC_OTG_CONTROL_SETUP;
}
/* start split */
qtd->complete_split = 0;
qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
qtd->isoc_split_offset = 0;
/* Store the qtd ptr in the urb to reference what QTD. */
urb->hcpriv = qtd;
return;
}
/**
* This function adds a QTD to the QTD-list of a QH. It will find the correct
* QH to place the QTD into. If it does not find a QH, then it will create a
* new QH. If the QH to which the QTD is added is not currently scheduled, it
* is placed into the proper schedule based on its EP type.
*
* @param[in] qtd The QTD to add
* @param[in] dwc_otg_hcd The DWC HCD structure
*
* @return 0 if successful, negative error code otherwise.
*/
int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *qtd,
dwc_otg_hcd_t *dwc_otg_hcd)
{
struct usb_host_endpoint *ep;
dwc_otg_qh_t *qh;
unsigned long flags;
int retval = 0;
struct urb *urb = qtd->urb;
SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags);
/*
* Get the QH which holds the QTD-list to insert to. Create QH if it
* doesn't exist.
*/
ep = dwc_urb_to_endpoint(urb);
qh = (dwc_otg_qh_t *)ep->hcpriv;
if (qh == NULL) {
qh = dwc_otg_hcd_qh_create (dwc_otg_hcd, urb);
if (qh == NULL) {
goto done;
}
ep->hcpriv = qh;
}
retval = dwc_otg_hcd_qh_add(dwc_otg_hcd, qh);
if (retval == 0) {
list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
}
done:
SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags);
return retval;
}
#endif /* DWC_DEVICE_ONLY */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,292 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $
* $Revision: #36 $
* $Date: 2008/09/26 $
* $Change: 1103515 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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 DWC_HOST_ONLY
#if !defined(__DWC_PCD_H__)
#define __DWC_PCD_H__
#include <linux/types.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
struct dwc_otg_device;
#include "otg_cil.h"
/**
* @file
*
* This file contains the structures, constants, and interfaces for
* the Perpherial Contoller Driver (PCD).
*
* The Peripheral Controller Driver (PCD) for Linux will implement the
* Gadget API, so that the existing Gadget drivers can be used. For
* the Mass Storage Function driver the File-backed USB Storage Gadget
* (FBS) driver will be used. The FBS driver supports the
* Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only
* transports.
*
*/
/** Invalid DMA Address */
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
/** Maxpacket size for EP0 */
#define MAX_EP0_SIZE 64
/** Maxpacket size for any EP */
#define MAX_PACKET_SIZE 1024
/** Max Transfer size for any EP */
#define MAX_TRANSFER_SIZE 65535
/** Max DMA Descriptor count for any EP */
#define MAX_DMA_DESC_CNT 64
/**
* Get the pointer to the core_if from the pcd pointer.
*/
#define GET_CORE_IF( _pcd ) (_pcd->otg_dev->core_if)
/**
* States of EP0.
*/
typedef enum ep0_state
{
EP0_DISCONNECT, /* no host */
EP0_IDLE,
EP0_IN_DATA_PHASE,
EP0_OUT_DATA_PHASE,
EP0_IN_STATUS_PHASE,
EP0_OUT_STATUS_PHASE,
EP0_STALL,
} ep0state_e;
/** Fordward declaration.*/
struct dwc_otg_pcd;
/** DWC_otg iso request structure.
*
*/
typedef struct usb_iso_request dwc_otg_pcd_iso_request_t;
/** PCD EP structure.
* This structure describes an EP, there is an array of EPs in the PCD
* structure.
*/
typedef struct dwc_otg_pcd_ep
{
/** USB EP data */
struct usb_ep ep;
/** USB EP Descriptor */
const struct usb_endpoint_descriptor *desc;
/** queue of dwc_otg_pcd_requests. */
struct list_head queue;
unsigned stopped : 1;
unsigned disabling : 1;
unsigned dma : 1;
unsigned queue_sof : 1;
#ifdef DWC_EN_ISOC
/** DWC_otg Isochronous Transfer */
struct usb_iso_request* iso_req;
#endif //DWC_EN_ISOC
/** DWC_otg ep data. */
dwc_ep_t dwc_ep;
/** Pointer to PCD */
struct dwc_otg_pcd *pcd;
}dwc_otg_pcd_ep_t;
/** DWC_otg PCD Structure.
* This structure encapsulates the data for the dwc_otg PCD.
*/
typedef struct dwc_otg_pcd
{
/** USB gadget */
struct usb_gadget gadget;
/** USB gadget driver pointer*/
struct usb_gadget_driver *driver;
/** The DWC otg device pointer. */
struct dwc_otg_device *otg_dev;
/** State of EP0 */
ep0state_e ep0state;
/** EP0 Request is pending */
unsigned ep0_pending : 1;
/** Indicates when SET CONFIGURATION Request is in process */
unsigned request_config : 1;
/** The state of the Remote Wakeup Enable. */
unsigned remote_wakeup_enable : 1;
/** The state of the B-Device HNP Enable. */
unsigned b_hnp_enable : 1;
/** The state of A-Device HNP Support. */
unsigned a_hnp_support : 1;
/** The state of the A-Device Alt HNP support. */
unsigned a_alt_hnp_support : 1;
/** Count of pending Requests */
unsigned request_pending;
/** SETUP packet for EP0
* This structure is allocated as a DMA buffer on PCD initialization
* with enough space for up to 3 setup packets.
*/
union
{
struct usb_ctrlrequest req;
uint32_t d32[2];
} *setup_pkt;
dma_addr_t setup_pkt_dma_handle;
/** 2-byte dma buffer used to return status from GET_STATUS */
uint16_t *status_buf;
dma_addr_t status_buf_dma_handle;
/** EP0 */
dwc_otg_pcd_ep_t ep0;
/** Array of IN EPs. */
dwc_otg_pcd_ep_t in_ep[ MAX_EPS_CHANNELS - 1];
/** Array of OUT EPs. */
dwc_otg_pcd_ep_t out_ep[ MAX_EPS_CHANNELS - 1];
/** number of valid EPs in the above array. */
// unsigned num_eps : 4;
spinlock_t lock;
/** Timer for SRP. If it expires before SRP is successful
* clear the SRP. */
struct timer_list srp_timer;
/** Tasklet to defer starting of TEST mode transmissions until
* Status Phase has been completed.
*/
struct tasklet_struct test_mode_tasklet;
/** Tasklet to delay starting of xfer in DMA mode */
struct tasklet_struct *start_xfer_tasklet;
/** The test mode to enter when the tasklet is executed. */
unsigned test_mode;
} dwc_otg_pcd_t;
/** DWC_otg request structure.
* This structure is a list of requests.
*/
typedef struct
{
struct usb_request req; /**< USB Request. */
struct list_head queue; /**< queue of these requests. */
} dwc_otg_pcd_request_t;
extern int dwc_otg_pcd_init(struct platform_device *pdev);
//extern void dwc_otg_pcd_remove( struct dwc_otg_device *_otg_dev );
extern void dwc_otg_pcd_remove( struct platform_device *pdev );
extern int32_t dwc_otg_pcd_handle_intr( dwc_otg_pcd_t *pcd );
extern void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t *pcd );
extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t *pcd);
extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t *pcd, int set);
extern void dwc_otg_iso_buffer_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_iso_request_t *req);
extern void dwc_otg_request_done(dwc_otg_pcd_ep_t *_ep, dwc_otg_pcd_request_t *req,
int status);
extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t *_ep);
extern void dwc_otg_pcd_update_otg(dwc_otg_pcd_t *_pcd,
const unsigned reset);
#ifndef VERBOSE
#define VERIFY_PCD_DMA_ADDR(_addr_) BUG_ON(((_addr_)==DMA_ADDR_INVALID)||\
((_addr_)==0)||\
((_addr_)&0x3))
#else
#define VERIFY_PCD_DMA_ADDR(_addr_) {\
if(((_addr_)==DMA_ADDR_INVALID)||\
((_addr_)==0)||\
((_addr_)&0x3)) {\
printk("%s: Invalid DMA address "#_addr_"(%.8x)\n",__func__,_addr_);\
BUG();\
}\
}
#endif
static inline void ep_check_and_patch_dma_addr(dwc_otg_pcd_ep_t *ep){
//void ep_check_and_patch_dma_addr(dwc_otg_pcd_ep_t *ep){
dwc_ep_t *dwc_ep=&ep->dwc_ep;
DWC_DEBUGPL(DBG_PCDV,"%s: dwc_ep xfer_buf=%.8x, total_len=%d, dma_addr=%.8x\n",__func__,(u32)dwc_ep->xfer_buff,(dwc_ep->total_len),dwc_ep->dma_addr);
if (/*(core_if->dma_enable)&&*/(dwc_ep->dma_addr==DMA_ADDR_INVALID)) {
if((((u32)dwc_ep->xfer_buff)&0x3)==0){
dwc_ep->dma_addr=dma_map_single(NULL,(void *)(dwc_ep->start_xfer_buff),(dwc_ep->total_len), DMA_TO_DEVICE);
DWC_DEBUGPL(DBG_PCDV," got dma_addr=%.8x\n",dwc_ep->dma_addr);
}else{
DWC_DEBUGPL(DBG_PCDV," buf not aligned, use aligned_buf instead. xfer_buf=%.8x, total_len=%d, aligned_buf_size=%d\n",(u32)dwc_ep->xfer_buff,(dwc_ep->total_len),dwc_ep->aligned_buf_size);
if(dwc_ep->aligned_buf_size<dwc_ep->total_len){
if(dwc_ep->aligned_buf){
//printk(" free buff dwc_ep aligned_buf_size=%d, aligned_buf(%.8x), aligned_dma_addr(%.8x));\n",dwc_ep->aligned_buf_size,dwc_ep->aligned_buf,dwc_ep->aligned_dma_addr);
//dma_free_coherent(NULL,dwc_ep->aligned_buf_size,dwc_ep->aligned_buf,dwc_ep->aligned_dma_addr);
kfree(dwc_ep->aligned_buf);
}
dwc_ep->aligned_buf_size=((1<<20)>(dwc_ep->total_len<<1))?(dwc_ep->total_len<<1):(1<<20);
//dwc_ep->aligned_buf = dma_alloc_coherent (NULL, dwc_ep->aligned_buf_size, &dwc_ep->aligned_dma_addr, GFP_KERNEL|GFP_DMA);
dwc_ep->aligned_buf=kmalloc(dwc_ep->aligned_buf_size,GFP_KERNEL|GFP_DMA|GFP_ATOMIC);
dwc_ep->aligned_dma_addr=dma_map_single(NULL,(void *)(dwc_ep->aligned_buf),(dwc_ep->aligned_buf_size),DMA_FROM_DEVICE);
if(!dwc_ep->aligned_buf){
DWC_ERROR("Cannot alloc required buffer!!\n");
BUG();
}
DWC_DEBUGPL(DBG_PCDV," dwc_ep allocated aligned buf=%.8x, dma_addr=%.8x, size=%d(0x%x)\n", (u32)dwc_ep->aligned_buf, dwc_ep->aligned_dma_addr, dwc_ep->aligned_buf_size, dwc_ep->aligned_buf_size);
}
dwc_ep->dma_addr=dwc_ep->aligned_dma_addr;
if(dwc_ep->is_in) {
memcpy(dwc_ep->aligned_buf,dwc_ep->xfer_buff,dwc_ep->total_len);
dma_sync_single_for_device(NULL,dwc_ep->dma_addr,dwc_ep->total_len,DMA_TO_DEVICE);
}
}
}
}
#endif
#endif /* DWC_HOST_ONLY */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,266 @@
/* ==========================================================================
* $File: //dwh/usb_iip/dev/software/otg/linux/platform/dwc_otg_plat.h $
* $Revision: #23 $
* $Date: 2008/07/15 $
* $Change: 1064915 $
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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.
* ========================================================================== */
#if !defined(__DWC_OTG_PLAT_H__)
#define __DWC_OTG_PLAT_H__
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <asm/io.h>
/* Changed all readl and writel to __raw_readl, __raw_writel */
/**
* @file
*
* This file contains the Platform Specific constants, interfaces
* (functions and macros) for Linux.
*
*/
//#if !defined(__LINUX_ARM_ARCH__)
//#error "The contents of this file is Linux specific!!!"
//#endif
/**
* Reads the content of a register.
*
* @param reg address of register to read.
* @return contents of the register.
*
* Usage:<br>
* <code>uint32_t dev_ctl = dwc_read_reg32(&dev_regs->dctl);</code>
*/
static __inline__ uint32_t dwc_read_reg32( volatile uint32_t *reg)
{
return __raw_readl(reg);
// return readl(reg);
};
/**
* Writes a register with a 32 bit value.
*
* @param reg address of register to read.
* @param value to write to _reg.
*
* Usage:<br>
* <code>dwc_write_reg32(&dev_regs->dctl, 0); </code>
*/
static __inline__ void dwc_write_reg32( volatile uint32_t *reg, const uint32_t value)
{
// writel( value, reg );
__raw_writel(value, reg);
};
/**
* This function modifies bit values in a register. Using the
* algorithm: (reg_contents & ~clear_mask) | set_mask.
*
* @param reg address of register to read.
* @param clear_mask bit mask to be cleared.
* @param set_mask bit mask to be set.
*
* Usage:<br>
* <code> // Clear the SOF Interrupt Mask bit and <br>
* // set the OTG Interrupt mask bit, leaving all others as they were.
* dwc_modify_reg32(&dev_regs->gintmsk, DWC_SOF_INT, DWC_OTG_INT);</code>
*/
static __inline__
void dwc_modify_reg32( volatile uint32_t *reg, const uint32_t clear_mask, const uint32_t set_mask)
{
// writel( (readl(reg) & ~clear_mask) | set_mask, reg );
__raw_writel( (__raw_readl(reg) & ~clear_mask) | set_mask, reg );
};
/**
* Wrapper for the OS micro-second delay function.
* @param[in] usecs Microseconds of delay
*/
static __inline__ void UDELAY( const uint32_t usecs )
{
udelay( usecs );
}
/**
* Wrapper for the OS milli-second delay function.
* @param[in] msecs milliseconds of delay
*/
static __inline__ void MDELAY( const uint32_t msecs )
{
mdelay( msecs );
}
/**
* Wrapper for the Linux spin_lock. On the ARM (Integrator)
* spin_lock() is a nop.
*
* @param lock Pointer to the spinlock.
*/
static __inline__ void SPIN_LOCK( spinlock_t *lock )
{
spin_lock(lock);
}
/**
* Wrapper for the Linux spin_unlock. On the ARM (Integrator)
* spin_lock() is a nop.
*
* @param lock Pointer to the spinlock.
*/
static __inline__ void SPIN_UNLOCK( spinlock_t *lock )
{
spin_unlock(lock);
}
/**
* Wrapper (macro) for the Linux spin_lock_irqsave. On the ARM
* (Integrator) spin_lock() is a nop.
*
* @param l Pointer to the spinlock.
* @param f unsigned long for irq flags storage.
*/
#define SPIN_LOCK_IRQSAVE( l, f ) spin_lock_irqsave(l,f);
/**
* Wrapper (macro) for the Linux spin_unlock_irqrestore. On the ARM
* (Integrator) spin_lock() is a nop.
*
* @param l Pointer to the spinlock.
* @param f unsigned long for irq flags storage.
*/
#define SPIN_UNLOCK_IRQRESTORE( l,f ) spin_unlock_irqrestore(l,f);
/*
* Debugging support vanishes in non-debug builds.
*/
/**
* The Debug Level bit-mask variable.
*/
extern uint32_t g_dbg_lvl;
/**
* Set the Debug Level variable.
*/
static inline uint32_t SET_DEBUG_LEVEL( const uint32_t new )
{
uint32_t old = g_dbg_lvl;
g_dbg_lvl = new;
return old;
}
/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */
#define DBG_CIL (0x2)
/** When debug level has the DBG_CILV bit set, display CIL Verbose debug
* messages */
#define DBG_CILV (0x20)
/** When debug level has the DBG_PCD bit set, display PCD (Device) debug
* messages */
#define DBG_PCD (0x4)
/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug
* messages */
#define DBG_PCDV (0x40)
/** When debug level has the DBG_HCD bit set, display Host debug messages */
#define DBG_HCD (0x8)
/** When debug level has the DBG_HCDV bit set, display Verbose Host debug
* messages */
#define DBG_HCDV (0x80)
/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host
* mode. */
#define DBG_HCD_URB (0x800)
/** When debug level has any bit set, display debug messages */
#define DBG_ANY (0xFF)
/** All debug messages off */
#define DBG_OFF 0
/** Prefix string for DWC_DEBUG print macros. */
#define USB_DWC "DWC_otg: "
/**
* Print a debug message when the Global debug level variable contains
* the bit defined in <code>lvl</code>.
*
* @param[in] lvl - Debug level, use one of the DBG_ constants above.
* @param[in] x - like printf
*
* Example:<p>
* <code>
* DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr);
* </code>
* <br>
* results in:<br>
* <code>
* usb-DWC_otg: dwc_otg_cil_init(ca867000)
* </code>
*/
#ifdef DEBUG
# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)printk( KERN_DEBUG USB_DWC x ); }while(0)
# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x )
# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl)
#else
# define DWC_DEBUGPL(lvl, x...) do{}while(0)
# define DWC_DEBUGP(x...)
# define CHK_DEBUG_LEVEL(level) (0)
#endif /*DEBUG*/
/**
* Print an Error message.
*/
#define DWC_ERROR(x...) printk( KERN_ERR USB_DWC x )
/**
* Print a Warning message.
*/
#define DWC_WARN(x...) printk( KERN_WARNING USB_DWC x )
/**
* Print a notice (normal but significant message).
*/
#define DWC_NOTICE(x...) printk( KERN_NOTICE USB_DWC x )
/**
* Basic message printing.
*/
#define DWC_PRINT(x...) printk( KERN_INFO USB_DWC x )
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_OTG_UTILS) += usb/
+obj-$(CONFIG_USB_DWC_OTG) += usb/dwc/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -134,6 +134,8 @@ source "drivers/usb/musb/Kconfig"
source "drivers/usb/renesas_usbhs/Kconfig"
+source "drivers/usb/dwc/Kconfig"
+
source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig"
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -17,7 +17,11 @@ static void urb_destroy(struct kref *kre
if (urb->transfer_flags & URB_FREE_BUFFER)
kfree(urb->transfer_buffer);
-
+ if (urb->aligned_transfer_buffer) {
+ kfree(urb->aligned_transfer_buffer);
+ urb->aligned_transfer_buffer = 0;
+ urb->aligned_transfer_dma = 0;
+ }
kfree(urb);
}
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1234,6 +1234,9 @@ struct urb {
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
+ void *aligned_transfer_buffer; /* (in) associeated data buffer */
+ dma_addr_t aligned_transfer_dma;/* (in) dma addr for transfer_buffer */
+ u32 aligned_transfer_buffer_length; /* (in) data buffer length */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -125,6 +125,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
#
choice
prompt "USB Peripheral Controller"
+ depends on !USB_DWC_OTG
help
A USB device uses a controller to talk to its host.
Systems should have only one such upstream link.
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -3,7 +3,7 @@
#
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
-obj-$(CONFIG_USB_GADGET) += udc-core.o
+#obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o