提交 6ed962a2 编写于 作者: G Greg Kroah-Hartman

Merge 3.1-rc4 into usb-next

This was done to resolve a conflict in this file:
	drivers/usb/host/xhci-ring.c
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
TODO
~~~~~~
Please pick something while reading :)
- Implement streaming support for BULK endpoints
Tatyana's patch "usb: Add streams support to the gadget framework"
introduces streaming support for the gadget driver.
Every usb_request has new field called stream_id which holds its id.
Every usb_ep has a field num_supported_strms which describes the max
number of streams supported (for this ep).
UAS is AFAIK the only gadget with streaming support.
- Convert interrupt handler to per-ep-thread-irq
As it turns out some DWC3-commands ~1ms to complete. Currently we spin
until the command completes which is bad.
Implementation idea:
- dwc core implements a demultiplexing irq chip for interrupts per
endpoint. The interrupt numbers are allocated during probe and belong
to the device. If MSI provides per-endpoint interrupt this dummy
interrupt chip can be replaced with "real" interrupts.
- interrupts are requested / allocated on usb_ep_enable() and removed on
usb_ep_disable(). Worst case are 32 interrupts, the lower limit is two
for ep0/1.
- dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout()
until the command completes.
- the interrupt handler is split into the following pieces:
- primary handler of the device
goes through every event and calls generic_handle_irq() for event
it. On return from generic_handle_irq() in acknowledges the event
counter so interrupt goes away (eventually).
- threaded handler of the device
none
- primary handler of the EP-interrupt
reads the event and tries to process it. Everything that requries
sleeping is handed over to the Thread. The event is saved in an
per-endpoint data-structure.
We probably have to pay attention not to process events once we
handed something to thread so we don't process event X prio Y
where X > Y.
- threaded handler of the EP-interrupt
handles the remaining EP work which might sleep such as waiting
for command completion.
Latency:
There should be no increase in latency since the interrupt-thread has a
high priority and will be run before an average task in user land
(except the user changed priorities).
......@@ -2139,6 +2139,14 @@ M: Matthew Garrett <mjg59@srcf.ucam.org>
S: Maintained
F: drivers/platform/x86/dell-wmi.c
DESIGNWARE USB3 DRD IP DRIVER
M: Felipe Balbi <balbi@ti.com>
L: linux-usb@vger.kernel.org
L: linux-omap@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
S: Maintained
F: drivers/usb/dwc3/
DEVICE NUMBER REGISTRY
M: Torben Mathiasen <device@lanana.org>
W: http://lanana.org/docs/device-list/index.html
......
......@@ -35,6 +35,13 @@ extern struct pxa_device_desc pxa168_device_fb;
extern struct pxa_device_desc pxa168_device_keypad;
extern struct pxa_device_desc pxa168_device_eth;
struct pxa168_usb_pdata {
/* If NULL, default phy init routine for PXA168 would be called */
int (*phy_init)(void __iomem *usb_phy_reg_base);
};
/* pdata can be NULL */
int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata);
static inline int pxa168_add_uart(int id)
{
struct pxa_device_desc *d = NULL;
......
......@@ -25,6 +25,9 @@
#include <mach/dma.h>
#include <mach/devices.h>
#include <mach/mfp.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <mach/pxa168.h>
#include "common.h"
#include "clock.h"
......@@ -83,6 +86,7 @@ static APBC_CLK(keypad, PXA168_KPC, 0, 32000);
static APMU_CLK(nand, NAND, 0x19b, 156000000);
static APMU_CLK(lcd, LCD, 0x7f, 312000000);
static APMU_CLK(eth, ETH, 0x09, 0);
static APMU_CLK(usb, USB, 0x12, 0);
/* device and clock bindings */
static struct clk_lookup pxa168_clkregs[] = {
......@@ -104,6 +108,7 @@ static struct clk_lookup pxa168_clkregs[] = {
INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL),
INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL),
INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"),
INIT_CLKREG(&clk_usb, "pxa168-ehci", "PXA168-USBCLK"),
};
static int __init pxa168_init(void)
......@@ -169,3 +174,44 @@ PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61);
PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8);
PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c);
PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff);
struct resource pxa168_usb_host_resources[] = {
/* USB Host conroller register base */
[0] = {
.start = 0xd4209000,
.end = 0xd4209000 + 0x200,
.flags = IORESOURCE_MEM,
.name = "pxa168-usb-host",
},
/* USB PHY register base */
[1] = {
.start = 0xd4206000,
.end = 0xd4206000 + 0xff,
.flags = IORESOURCE_MEM,
.name = "pxa168-usb-phy",
},
[2] = {
.start = IRQ_PXA168_USB2,
.end = IRQ_PXA168_USB2,
.flags = IORESOURCE_IRQ,
},
};
static u64 pxa168_usb_host_dmamask = DMA_BIT_MASK(32);
struct platform_device pxa168_device_usb_host = {
.name = "pxa168-ehci",
.id = -1,
.dev = {
.dma_mask = &pxa168_usb_host_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.num_resources = ARRAY_SIZE(pxa168_usb_host_resources),
.resource = pxa168_usb_host_resources,
};
int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata)
{
pxa168_device_usb_host.dev.platform_data = pdata;
return platform_device_register(&pxa168_device_usb_host);
}
......@@ -69,6 +69,7 @@ config USB_ARCH_HAS_EHCI
default y if ARCH_MSM
default y if MICROBLAZE
default y if SPARC_LEON
default y if ARCH_MMP
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
......@@ -110,6 +111,8 @@ config USB
source "drivers/usb/core/Kconfig"
source "drivers/usb/dwc3/Kconfig"
source "drivers/usb/mon/Kconfig"
source "drivers/usb/wusbcore/Kconfig"
......
......@@ -6,6 +6,8 @@
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_PCI) += host/
......
......@@ -1058,11 +1058,11 @@ static int acm_probe(struct usb_interface *intf,
goto alloc_fail;
}
ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
readsize = le16_to_cpu(epread->wMaxPacketSize) *
ctrlsize = usb_endpoint_maxp(epctrl);
readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 2);
acm->combined_interfaces = combined_interfaces;
acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
acm->writesize = usb_endpoint_maxp(epwrite) * 20;
acm->control = control_interface;
acm->data = data_interface;
acm->minor = minor;
......
......@@ -682,7 +682,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (!ep || !usb_endpoint_is_int_in(ep))
goto err;
desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
......
......@@ -186,8 +186,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++)
if (current_setting->endpoint[n].desc.bEndpointAddress ==
data->bulk_in)
max_size = le16_to_cpu(current_setting->endpoint[n].
desc.wMaxPacketSize);
max_size = usb_endpoint_maxp(&current_setting->endpoint[n].desc);
if (max_size == 0) {
dev_err(dev, "Couldn't get wMaxPacketSize\n");
......@@ -636,7 +635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {
desc = &current_setting->endpoint[n].desc;
if (desc->bEndpointAddress == data->bulk_in)
max_size = le16_to_cpu(desc->wMaxPacketSize);
max_size = usb_endpoint_maxp(desc);
}
if (max_size == 0) {
......
......@@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
if (usb_endpoint_xfer_isoc(&ep->desc))
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
le16_to_cpu(ep->desc.wMaxPacketSize);
usb_endpoint_maxp(&ep->desc);
else if (usb_endpoint_xfer_int(&ep->desc))
max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) *
max_tx = usb_endpoint_maxp(&ep->desc) *
(desc->bMaxBurst + 1);
else
max_tx = 999999;
......@@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
cfgno, inum, asnum, d->bEndpointAddress);
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
endpoint->desc.bInterval = 1;
if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
if (usb_endpoint_maxp(&endpoint->desc) > 8)
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
}
......@@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
&& usb_endpoint_xfer_bulk(d)) {
unsigned maxp;
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff;
maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;
if (maxp != 512)
dev_warn(ddev, "config %d interface %d altsetting %d "
"bulk endpoint 0x%X has invalid maxpacket %d\n",
......
......@@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
dir = usb_endpoint_dir_in(desc) ? 'I' : 'O';
if (speed == USB_SPEED_HIGH) {
switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
switch (usb_endpoint_maxp(desc) & (0x03 << 11)) {
case 1 << 11:
bandwidth = 2; break;
case 2 << 11:
......@@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
(le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) *
(usb_endpoint_maxp(desc) & 0x07ff) *
bandwidth,
interval, unit);
return start;
......
......@@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev,
{
struct ep_device *ep = to_ep_device(dev);
return sprintf(buf, "%04x\n",
le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
usb_endpoint_maxp(ep->desc) & 0x07ff);
}
static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
......
......@@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev)
int i;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
if (!udev) {
pr_debug ("%s nodev\n", __func__);
return;
}
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
* this quiesces everything except pending urbs.
......@@ -3023,7 +3018,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
......
......@@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
max = le16_to_cpu(ep->desc.wMaxPacketSize);
max = usb_endpoint_maxp(&ep->desc);
if (max <= 0) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
......
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET)
select USB_OTG_UTILS
help
Say Y or M here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
If you choose to build this driver is a dynamically linked
module, the module will be called dwc3.ko.
if USB_DWC3
config USB_DWC3_DEBUG
bool "Enable Debugging Messages"
help
Say Y here to enable debugging messages on DWC3 Driver.
config USB_DWC3_VERBOSE
bool "Enable Verbose Debugging Messages"
depends on USB_DWC3_DEBUG
help
Say Y here to enable verbose debugging messages on DWC3 Driver.
endif
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
ifneq ($(CONFIG_USB_GADGET_DWC3),)
dwc3-y += gadget.o ep0.o
endif
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
endif
##
# Platform-specific glue layers go here
#
# NOTICE: Make sure your glue layer doesn't depend on anything
# which is arch-specific and that it compiles on all situations.
#
# We want to keep this requirement in order to be able to compile
# the entire driver (with all its glue layers) on several architectures
# and make sure it compiles fine. This will also help with allmodconfig
# and allyesconfig builds.
#
# The only exception is the PCI glue layer, but that's only because
# PCI doesn't provide nops if CONFIG_PCI isn't enabled.
##
obj-$(CONFIG_USB_DWC3) += dwc3-omap.o
ifneq ($(CONFIG_PCI),)
obj-$(CONFIG_USB_DWC3) += dwc3-pci.o
endif
/**
* core.c - DesignWare USB3 DRD Controller Core file
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
#include "debug.h"
/**
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
*/
static void dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
/* Before Resetting PHY, put Core in Reset */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
/* Assert USB3 PHY reset */
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
/* Assert USB2 PHY reset */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
mdelay(100);
/* Clear USB3 PHY reset */
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
/* Clear USB2 PHY reset */
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
/* After PHYs are stable we can take Core out of reset state */
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
/**
* dwc3_free_one_event_buffer - Frees one event buffer
* @dwc: Pointer to our controller context structure
* @evt: Pointer to event buffer to be freed
*/
static void dwc3_free_one_event_buffer(struct dwc3 *dwc,
struct dwc3_event_buffer *evt)
{
dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma);
kfree(evt);
}
/**
* dwc3_alloc_one_event_buffer - Allocated one event buffer structure
* @dwc: Pointer to our controller context structure
* @length: size of the event buffer
*
* Returns a pointer to the allocated event buffer structure on succes
* otherwise ERR_PTR(errno).
*/
static struct dwc3_event_buffer *__devinit
dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length)
{
struct dwc3_event_buffer *evt;
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
if (!evt)
return ERR_PTR(-ENOMEM);
evt->dwc = dwc;
evt->length = length;
evt->buf = dma_alloc_coherent(dwc->dev, length,
&evt->dma, GFP_KERNEL);
if (!evt->buf) {
kfree(evt);
return ERR_PTR(-ENOMEM);
}
return evt;
}
/**
* dwc3_free_event_buffers - frees all allocated event buffers
* @dwc: Pointer to our controller context structure
*/
static void dwc3_free_event_buffers(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int i;
for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) {
evt = dwc->ev_buffs[i];
if (evt) {
dwc3_free_one_event_buffer(dwc, evt);
dwc->ev_buffs[i] = NULL;
}
}
}
/**
* dwc3_alloc_event_buffers - Allocates @num event buffers of size @length
* @dwc: Pointer to out controller context structure
* @num: number of event buffers to allocate
* @length: size of event buffer
*
* Returns 0 on success otherwise negative errno. In error the case, dwc
* may contain some buffers allocated but not all which were requested.
*/
static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num,
unsigned length)
{
int i;
for (i = 0; i < num; i++) {
struct dwc3_event_buffer *evt;
evt = dwc3_alloc_one_event_buffer(dwc, length);
if (IS_ERR(evt)) {
dev_err(dwc->dev, "can't allocate event buffer\n");
return PTR_ERR(evt);
}
dwc->ev_buffs[i] = evt;
}
return 0;
}
/**
* dwc3_event_buffers_setup - setup our allocated event buffers
* @dwc: Pointer to out controller context structure
*
* Returns 0 on success otherwise negative errno.
*/
static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
evt = dwc->ev_buffs[n];
dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
evt->buf, (unsigned long long) evt->dma,
evt->length);
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n),
lower_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n),
upper_32_bits(evt->dma));
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n),
evt->length & 0xffff);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
}
return 0;
}
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
int n;
for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) {
evt = dwc->ev_buffs[n];
dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0);
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
}
}
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
*
* Returns 0 on success otherwise negative errno.
*/
static int __devinit dwc3_core_init(struct dwc3 *dwc)
{
unsigned long timeout;
u32 reg;
int ret;
dwc3_core_soft_reset(dwc);
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
do {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
break;
if (time_after(jiffies, timeout)) {
dev_err(dwc->dev, "Reset Timed Out\n");
ret = -ETIMEDOUT;
goto err0;
}
cpu_relax();
} while (true);
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
/* This should read as U3 followed by revision number */
if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) {
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
ret = -ENODEV;
goto err0;
}
dwc->revision = reg & DWC3_GSNPSREV_MASK;
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM,
DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM;
goto err1;
}
ret = dwc3_event_buffers_setup(dwc);
if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n");
goto err1;
}
return 0;
err1:
dwc3_free_event_buffers(dwc);
err0:
return ret;
}
static void dwc3_core_exit(struct dwc3 *dwc)
{
dwc3_event_buffers_cleanup(dwc);
dwc3_free_event_buffers(dwc);
}
#define DWC3_ALIGN_MASK (16 - 1)
static int __devinit dwc3_probe(struct platform_device *pdev)
{
const struct platform_device_id *id = platform_get_device_id(pdev);
struct resource *res;
struct dwc3 *dwc;
void __iomem *regs;
unsigned int features = id->driver_data;
int ret = -ENOMEM;
int irq;
void *mem;
mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL);
if (!mem) {
dev_err(&pdev->dev, "not enough memory\n");
goto err0;
}
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing resource\n");
goto err1;
}
res = request_mem_region(res->start, resource_size(res),
dev_name(&pdev->dev));
if (!res) {
dev_err(&pdev->dev, "can't request mem region\n");
goto err1;
}
regs = ioremap(res->start, resource_size(res));
if (!regs) {
dev_err(&pdev->dev, "ioremap failed\n");
goto err2;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing IRQ\n");
goto err3;
}
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dwc->regs = regs;
dwc->regs_size = resource_size(res);
dwc->dev = &pdev->dev;
dwc->irq = irq;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_forbid(&pdev->dev);
ret = dwc3_core_init(dwc);
if (ret) {
dev_err(&pdev->dev, "failed to initialize core\n");
goto err3;
}
if (features & DWC3_HAS_PERIPHERAL) {
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(&pdev->dev, "failed to initialized gadget\n");
goto err4;
}
}
ret = dwc3_debugfs_init(dwc);
if (ret) {
dev_err(&pdev->dev, "failed to initialize debugfs\n");
goto err5;
}
pm_runtime_allow(&pdev->dev);
return 0;
err5:
if (features & DWC3_HAS_PERIPHERAL)
dwc3_gadget_exit(dwc);
err4:
dwc3_core_exit(dwc);
err3:
iounmap(regs);
err2:
release_mem_region(res->start, resource_size(res));
err1:
kfree(dwc->mem);
err0:
return ret;
}
static int __devexit dwc3_remove(struct platform_device *pdev)
{
const struct platform_device_id *id = platform_get_device_id(pdev);
struct dwc3 *dwc = platform_get_drvdata(pdev);
struct resource *res;
unsigned int features = id->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
dwc3_debugfs_exit(dwc);
if (features & DWC3_HAS_PERIPHERAL)
dwc3_gadget_exit(dwc);
dwc3_core_exit(dwc);
release_mem_region(res->start, resource_size(res));
iounmap(dwc->regs);
kfree(dwc->mem);
return 0;
}
static const struct platform_device_id dwc3_id_table[] __devinitconst = {
{
.name = "dwc3-omap",
.driver_data = (DWC3_HAS_PERIPHERAL
| DWC3_HAS_XHCI
| DWC3_HAS_OTG),
},
{
.name = "dwc3-pci",
.driver_data = DWC3_HAS_PERIPHERAL,
},
{ }, /* Terminating Entry */
};
MODULE_DEVICE_TABLE(platform, dwc3_id_table);
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
.remove = __devexit_p(dwc3_remove),
.driver = {
.name = "dwc3",
},
.id_table = dwc3_id_table,
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver");
static int __devinit dwc3_init(void)
{
return platform_driver_register(&dwc3_driver);
}
module_init(dwc3_init);
static void __exit dwc3_exit(void)
{
platform_driver_unregister(&dwc3_driver);
}
module_exit(dwc3_exit);
此差异已折叠。
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core.h"
#ifdef CONFIG_DEBUG_FS
extern int dwc3_debugfs_init(struct dwc3 *);
extern void dwc3_debugfs_exit(struct dwc3 *);
#else
static inline int dwc3_debugfs_init(struct dwc3 *d)
{ return 0; }
static inline void dwc3_debugfs_exit(struct dwc3 *d)
{ }
#endif
/**
* debugfs.c - DesignWare USB3 DRD Controller DebugFS file
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
struct dwc3_register {
const char *name;
u32 offset;
};
#define dump_register(nm) \
{ \
.name = __stringify(nm), \
.offset = DWC3_ ##nm, \
}
static const struct dwc3_register dwc3_regs[] = {
dump_register(GSBUSCFG0),
dump_register(GSBUSCFG1),
dump_register(GTXTHRCFG),
dump_register(GRXTHRCFG),
dump_register(GCTL),
dump_register(GEVTEN),
dump_register(GSTS),
dump_register(GSNPSID),
dump_register(GGPIO),
dump_register(GUID),
dump_register(GUCTL),
dump_register(GBUSERRADDR0),
dump_register(GBUSERRADDR1),
dump_register(GPRTBIMAP0),
dump_register(GPRTBIMAP1),
dump_register(GHWPARAMS0),
dump_register(GHWPARAMS1),
dump_register(GHWPARAMS2),
dump_register(GHWPARAMS3),
dump_register(GHWPARAMS4),
dump_register(GHWPARAMS5),
dump_register(GHWPARAMS6),
dump_register(GHWPARAMS7),
dump_register(GDBGFIFOSPACE),
dump_register(GDBGLTSSM),
dump_register(GPRTBIMAP_HS0),
dump_register(GPRTBIMAP_HS1),
dump_register(GPRTBIMAP_FS0),
dump_register(GPRTBIMAP_FS1),
dump_register(GUSB2PHYCFG(0)),
dump_register(GUSB2PHYCFG(1)),
dump_register(GUSB2PHYCFG(2)),
dump_register(GUSB2PHYCFG(3)),
dump_register(GUSB2PHYCFG(4)),
dump_register(GUSB2PHYCFG(5)),
dump_register(GUSB2PHYCFG(6)),
dump_register(GUSB2PHYCFG(7)),
dump_register(GUSB2PHYCFG(8)),
dump_register(GUSB2PHYCFG(9)),
dump_register(GUSB2PHYCFG(10)),
dump_register(GUSB2PHYCFG(11)),
dump_register(GUSB2PHYCFG(12)),
dump_register(GUSB2PHYCFG(13)),
dump_register(GUSB2PHYCFG(14)),
dump_register(GUSB2PHYCFG(15)),
dump_register(GUSB2I2CCTL(0)),
dump_register(GUSB2I2CCTL(1)),
dump_register(GUSB2I2CCTL(2)),
dump_register(GUSB2I2CCTL(3)),
dump_register(GUSB2I2CCTL(4)),
dump_register(GUSB2I2CCTL(5)),
dump_register(GUSB2I2CCTL(6)),
dump_register(GUSB2I2CCTL(7)),
dump_register(GUSB2I2CCTL(8)),
dump_register(GUSB2I2CCTL(9)),
dump_register(GUSB2I2CCTL(10)),
dump_register(GUSB2I2CCTL(11)),
dump_register(GUSB2I2CCTL(12)),
dump_register(GUSB2I2CCTL(13)),
dump_register(GUSB2I2CCTL(14)),
dump_register(GUSB2I2CCTL(15)),
dump_register(GUSB2PHYACC(0)),
dump_register(GUSB2PHYACC(1)),
dump_register(GUSB2PHYACC(2)),
dump_register(GUSB2PHYACC(3)),
dump_register(GUSB2PHYACC(4)),
dump_register(GUSB2PHYACC(5)),
dump_register(GUSB2PHYACC(6)),
dump_register(GUSB2PHYACC(7)),
dump_register(GUSB2PHYACC(8)),
dump_register(GUSB2PHYACC(9)),
dump_register(GUSB2PHYACC(10)),
dump_register(GUSB2PHYACC(11)),
dump_register(GUSB2PHYACC(12)),
dump_register(GUSB2PHYACC(13)),
dump_register(GUSB2PHYACC(14)),
dump_register(GUSB2PHYACC(15)),
dump_register(GUSB3PIPECTL(0)),
dump_register(GUSB3PIPECTL(1)),
dump_register(GUSB3PIPECTL(2)),
dump_register(GUSB3PIPECTL(3)),
dump_register(GUSB3PIPECTL(4)),
dump_register(GUSB3PIPECTL(5)),
dump_register(GUSB3PIPECTL(6)),
dump_register(GUSB3PIPECTL(7)),
dump_register(GUSB3PIPECTL(8)),
dump_register(GUSB3PIPECTL(9)),
dump_register(GUSB3PIPECTL(10)),
dump_register(GUSB3PIPECTL(11)),
dump_register(GUSB3PIPECTL(12)),
dump_register(GUSB3PIPECTL(13)),
dump_register(GUSB3PIPECTL(14)),
dump_register(GUSB3PIPECTL(15)),
dump_register(GTXFIFOSIZ(0)),
dump_register(GTXFIFOSIZ(1)),
dump_register(GTXFIFOSIZ(2)),
dump_register(GTXFIFOSIZ(3)),
dump_register(GTXFIFOSIZ(4)),
dump_register(GTXFIFOSIZ(5)),
dump_register(GTXFIFOSIZ(6)),
dump_register(GTXFIFOSIZ(7)),
dump_register(GTXFIFOSIZ(8)),
dump_register(GTXFIFOSIZ(9)),
dump_register(GTXFIFOSIZ(10)),
dump_register(GTXFIFOSIZ(11)),
dump_register(GTXFIFOSIZ(12)),
dump_register(GTXFIFOSIZ(13)),
dump_register(GTXFIFOSIZ(14)),
dump_register(GTXFIFOSIZ(15)),
dump_register(GTXFIFOSIZ(16)),
dump_register(GTXFIFOSIZ(17)),
dump_register(GTXFIFOSIZ(18)),
dump_register(GTXFIFOSIZ(19)),
dump_register(GTXFIFOSIZ(20)),
dump_register(GTXFIFOSIZ(21)),
dump_register(GTXFIFOSIZ(22)),
dump_register(GTXFIFOSIZ(23)),
dump_register(GTXFIFOSIZ(24)),
dump_register(GTXFIFOSIZ(25)),
dump_register(GTXFIFOSIZ(26)),
dump_register(GTXFIFOSIZ(27)),
dump_register(GTXFIFOSIZ(28)),
dump_register(GTXFIFOSIZ(29)),
dump_register(GTXFIFOSIZ(30)),
dump_register(GTXFIFOSIZ(31)),
dump_register(GRXFIFOSIZ(0)),
dump_register(GRXFIFOSIZ(1)),
dump_register(GRXFIFOSIZ(2)),
dump_register(GRXFIFOSIZ(3)),
dump_register(GRXFIFOSIZ(4)),
dump_register(GRXFIFOSIZ(5)),
dump_register(GRXFIFOSIZ(6)),
dump_register(GRXFIFOSIZ(7)),
dump_register(GRXFIFOSIZ(8)),
dump_register(GRXFIFOSIZ(9)),
dump_register(GRXFIFOSIZ(10)),
dump_register(GRXFIFOSIZ(11)),
dump_register(GRXFIFOSIZ(12)),
dump_register(GRXFIFOSIZ(13)),
dump_register(GRXFIFOSIZ(14)),
dump_register(GRXFIFOSIZ(15)),
dump_register(GRXFIFOSIZ(16)),
dump_register(GRXFIFOSIZ(17)),
dump_register(GRXFIFOSIZ(18)),
dump_register(GRXFIFOSIZ(19)),
dump_register(GRXFIFOSIZ(20)),
dump_register(GRXFIFOSIZ(21)),
dump_register(GRXFIFOSIZ(22)),
dump_register(GRXFIFOSIZ(23)),
dump_register(GRXFIFOSIZ(24)),
dump_register(GRXFIFOSIZ(25)),
dump_register(GRXFIFOSIZ(26)),
dump_register(GRXFIFOSIZ(27)),
dump_register(GRXFIFOSIZ(28)),
dump_register(GRXFIFOSIZ(29)),
dump_register(GRXFIFOSIZ(30)),
dump_register(GRXFIFOSIZ(31)),
dump_register(GEVNTADRLO(0)),
dump_register(GEVNTADRHI(0)),
dump_register(GEVNTSIZ(0)),
dump_register(GEVNTCOUNT(0)),
dump_register(GHWPARAMS8),
dump_register(DCFG),
dump_register(DCTL),
dump_register(DEVTEN),
dump_register(DSTS),
dump_register(DGCMDPAR),
dump_register(DGCMD),
dump_register(DALEPENA),
dump_register(DEPCMDPAR2(0)),
dump_register(DEPCMDPAR2(1)),
dump_register(DEPCMDPAR2(2)),
dump_register(DEPCMDPAR2(3)),
dump_register(DEPCMDPAR2(4)),
dump_register(DEPCMDPAR2(5)),
dump_register(DEPCMDPAR2(6)),
dump_register(DEPCMDPAR2(7)),
dump_register(DEPCMDPAR2(8)),
dump_register(DEPCMDPAR2(9)),
dump_register(DEPCMDPAR2(10)),
dump_register(DEPCMDPAR2(11)),
dump_register(DEPCMDPAR2(12)),
dump_register(DEPCMDPAR2(13)),
dump_register(DEPCMDPAR2(14)),
dump_register(DEPCMDPAR2(15)),
dump_register(DEPCMDPAR2(16)),
dump_register(DEPCMDPAR2(17)),
dump_register(DEPCMDPAR2(18)),
dump_register(DEPCMDPAR2(19)),
dump_register(DEPCMDPAR2(20)),
dump_register(DEPCMDPAR2(21)),
dump_register(DEPCMDPAR2(22)),
dump_register(DEPCMDPAR2(23)),
dump_register(DEPCMDPAR2(24)),
dump_register(DEPCMDPAR2(25)),
dump_register(DEPCMDPAR2(26)),
dump_register(DEPCMDPAR2(27)),
dump_register(DEPCMDPAR2(28)),
dump_register(DEPCMDPAR2(29)),
dump_register(DEPCMDPAR2(30)),
dump_register(DEPCMDPAR2(31)),
dump_register(DEPCMDPAR1(0)),
dump_register(DEPCMDPAR1(1)),
dump_register(DEPCMDPAR1(2)),
dump_register(DEPCMDPAR1(3)),
dump_register(DEPCMDPAR1(4)),
dump_register(DEPCMDPAR1(5)),
dump_register(DEPCMDPAR1(6)),
dump_register(DEPCMDPAR1(7)),
dump_register(DEPCMDPAR1(8)),
dump_register(DEPCMDPAR1(9)),
dump_register(DEPCMDPAR1(10)),
dump_register(DEPCMDPAR1(11)),
dump_register(DEPCMDPAR1(12)),
dump_register(DEPCMDPAR1(13)),
dump_register(DEPCMDPAR1(14)),
dump_register(DEPCMDPAR1(15)),
dump_register(DEPCMDPAR1(16)),
dump_register(DEPCMDPAR1(17)),
dump_register(DEPCMDPAR1(18)),
dump_register(DEPCMDPAR1(19)),
dump_register(DEPCMDPAR1(20)),
dump_register(DEPCMDPAR1(21)),
dump_register(DEPCMDPAR1(22)),
dump_register(DEPCMDPAR1(23)),
dump_register(DEPCMDPAR1(24)),
dump_register(DEPCMDPAR1(25)),
dump_register(DEPCMDPAR1(26)),
dump_register(DEPCMDPAR1(27)),
dump_register(DEPCMDPAR1(28)),
dump_register(DEPCMDPAR1(29)),
dump_register(DEPCMDPAR1(30)),
dump_register(DEPCMDPAR1(31)),
dump_register(DEPCMDPAR0(0)),
dump_register(DEPCMDPAR0(1)),
dump_register(DEPCMDPAR0(2)),
dump_register(DEPCMDPAR0(3)),
dump_register(DEPCMDPAR0(4)),
dump_register(DEPCMDPAR0(5)),
dump_register(DEPCMDPAR0(6)),
dump_register(DEPCMDPAR0(7)),
dump_register(DEPCMDPAR0(8)),
dump_register(DEPCMDPAR0(9)),
dump_register(DEPCMDPAR0(10)),
dump_register(DEPCMDPAR0(11)),
dump_register(DEPCMDPAR0(12)),
dump_register(DEPCMDPAR0(13)),
dump_register(DEPCMDPAR0(14)),
dump_register(DEPCMDPAR0(15)),
dump_register(DEPCMDPAR0(16)),
dump_register(DEPCMDPAR0(17)),
dump_register(DEPCMDPAR0(18)),
dump_register(DEPCMDPAR0(19)),
dump_register(DEPCMDPAR0(20)),
dump_register(DEPCMDPAR0(21)),
dump_register(DEPCMDPAR0(22)),
dump_register(DEPCMDPAR0(23)),
dump_register(DEPCMDPAR0(24)),
dump_register(DEPCMDPAR0(25)),
dump_register(DEPCMDPAR0(26)),
dump_register(DEPCMDPAR0(27)),
dump_register(DEPCMDPAR0(28)),
dump_register(DEPCMDPAR0(29)),
dump_register(DEPCMDPAR0(30)),
dump_register(DEPCMDPAR0(31)),
dump_register(DEPCMD(0)),
dump_register(DEPCMD(1)),
dump_register(DEPCMD(2)),
dump_register(DEPCMD(3)),
dump_register(DEPCMD(4)),
dump_register(DEPCMD(5)),
dump_register(DEPCMD(6)),
dump_register(DEPCMD(7)),
dump_register(DEPCMD(8)),
dump_register(DEPCMD(9)),
dump_register(DEPCMD(10)),
dump_register(DEPCMD(11)),
dump_register(DEPCMD(12)),
dump_register(DEPCMD(13)),
dump_register(DEPCMD(14)),
dump_register(DEPCMD(15)),
dump_register(DEPCMD(16)),
dump_register(DEPCMD(17)),
dump_register(DEPCMD(18)),
dump_register(DEPCMD(19)),
dump_register(DEPCMD(20)),
dump_register(DEPCMD(21)),
dump_register(DEPCMD(22)),
dump_register(DEPCMD(23)),
dump_register(DEPCMD(24)),
dump_register(DEPCMD(25)),
dump_register(DEPCMD(26)),
dump_register(DEPCMD(27)),
dump_register(DEPCMD(28)),
dump_register(DEPCMD(29)),
dump_register(DEPCMD(30)),
dump_register(DEPCMD(31)),
dump_register(OCFG),
dump_register(OCTL),
dump_register(OEVTEN),
dump_register(OSTS),
};
static int dwc3_regdump_show(struct seq_file *s, void *unused)
{
struct dwc3 *dwc = s->private;
int i;
seq_printf(s, "DesignWare USB3 Core Register Dump\n");
for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) {
seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name,
dwc3_readl(dwc->regs, dwc3_regs[i].offset));
}
return 0;
}
static int dwc3_regdump_open(struct inode *inode, struct file *file)
{
return single_open(file, dwc3_regdump_show, inode->i_private);
}
static const struct file_operations dwc3_regdump_fops = {
.open = dwc3_regdump_open,
.read = seq_read,
.release = single_release,
};
static int dwc3_send_testmode_cmd(struct dwc3 *dwc, int mode)
{
u32 timeout = 250;
dwc3_writel(dwc->regs, DWC3_DGCMDPAR, mode);
dwc3_writel(dwc->regs, DWC3_DGCMD, DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK |
DWC3_DEPCMD_CMDACT);
do {
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
if (!(reg & DWC3_DEPCMD_CMDACT))
return 0;
timeout--;
if (!timeout)
return -ETIMEDOUT;
mdelay(1);
} while (1);
}
static struct dwc3_trb_hw trb_0 __aligned(16);
static struct dwc3_trb_hw trb_1 __aligned(16);
#define BUF_SIZE 4096
static int dwc3_testmode_open(struct inode *inode, struct file *file)
{
struct dwc3 *dwc = inode->i_private;
struct dwc3_gadget_ep_cmd_params par0;
struct dwc3_gadget_ep_cmd_params par1;
struct dwc3_trb trb;
int ret;
u8 *buf0;
u8 *buf1;
buf0 = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf0)
return -ENOMEM;
buf1 = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!buf1)
return -ENOMEM;
memset(buf0, 0xaa, BUF_SIZE);
memset(buf1, 0x33, BUF_SIZE);
memset(&trb, 0, sizeof(trb));
memset(&par0, 0, sizeof(par0));
memset(&par1, 0, sizeof(par1));
trb.lst = 1;
trb.trbctl = DWC3_TRBCTL_NORMAL;
trb.length = BUF_SIZE;
trb.hwo = 1;
trb.bplh = virt_to_phys(buf0);
dwc3_trb_to_hw(&trb, &trb_0);
trb.bplh = virt_to_phys(buf1);
dwc3_trb_to_hw(&trb, &trb_1);
par0.param0.depstrtxfer.transfer_desc_addr_high =
upper_32_bits(virt_to_phys(&trb_0));
par0.param1.depstrtxfer.transfer_desc_addr_low =
lower_32_bits(virt_to_phys(&trb_0));
par1.param0.depstrtxfer.transfer_desc_addr_high =
upper_32_bits(virt_to_phys(&trb_1));
par1.param1.depstrtxfer.transfer_desc_addr_low =
lower_32_bits(virt_to_phys(&trb_1));
dwc3_send_testmode_cmd(dwc, 1);
ret = dwc3_send_gadget_ep_cmd(dwc, 0, DWC3_DEPCMD_STARTTRANSFER, &par0);
ret = dwc3_send_gadget_ep_cmd(dwc, 1, DWC3_DEPCMD_STARTTRANSFER, &par1);
dwc3_send_testmode_cmd(dwc, 0);
return -EBUSY;
}
static const struct file_operations dwc3_testmode_fops = {
.open = dwc3_testmode_open,
.read = seq_read,
.release = single_release,
};
int __devinit dwc3_debugfs_init(struct dwc3 *dwc)
{
struct dentry *root;
struct dentry *file;
int ret;
root = debugfs_create_dir(dev_name(dwc->dev), NULL);
if (IS_ERR(root)){
ret = PTR_ERR(root);
goto err0;
}
dwc->root = root;
file = debugfs_create_file("regdump", S_IRUGO, root, dwc,
&dwc3_regdump_fops);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err1;
}
file = debugfs_create_file("testmode", S_IRUGO, root, dwc,
&dwc3_testmode_fops);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
goto err1;
}
return 0;
err1:
debugfs_remove_recursive(root);
err0:
return ret;
}
void __devexit dwc3_debugfs_exit(struct dwc3 *dwc)
{
debugfs_remove_recursive(dwc->root);
dwc->root = NULL;
}
/**
* dwc3-omap.c - OMAP Specific Glue layer
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include "io.h"
/*
* All these registers belong to OMAP's Wrapper around the
* DesignWare USB3 Core.
*/
#define USBOTGSS_REVISION 0x0000
#define USBOTGSS_SYSCONFIG 0x0010
#define USBOTGSS_IRQ_EOI 0x0020
#define USBOTGSS_IRQSTATUS_RAW_0 0x0024
#define USBOTGSS_IRQSTATUS_0 0x0028
#define USBOTGSS_IRQENABLE_SET_0 0x002c
#define USBOTGSS_IRQENABLE_CLR_0 0x0030
#define USBOTGSS_IRQSTATUS_RAW_1 0x0034
#define USBOTGSS_IRQSTATUS_1 0x0038
#define USBOTGSS_IRQENABLE_SET_1 0x003c
#define USBOTGSS_IRQENABLE_CLR_1 0x0040
#define USBOTGSS_UTMI_OTG_CTRL 0x0080
#define USBOTGSS_UTMI_OTG_STATUS 0x0084
#define USBOTGSS_MMRAM_OFFSET 0x0100
#define USBOTGSS_FLADJ 0x0104
#define USBOTGSS_DEBUG_CFG 0x0108
#define USBOTGSS_DEBUG_DATA 0x010c
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4)
#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2)
/* IRQ_EOI REGISTER */
#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0)
/* IRQS0 BITS */
#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
/* IRQ1 BITS */
#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17)
#define USBOTGSS_IRQ1_OEVT (1 << 16)
#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13)
#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12)
#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11)
#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8)
#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5)
#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4)
#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3)
#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0)
/* UTMI_OTG_CTRL REGISTER */
#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5)
#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4)
#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3)
#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0)
/* UTMI_OTG_STATUS REGISTER */
#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31)
#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9)
#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4)
#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3)
#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2)
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
struct dwc3_omap {
/* device lock */
spinlock_t lock;
struct platform_device *dwc3;
struct device *dev;
int irq;
void __iomem *base;
void *context;
u32 resource_size;
u32 dma_status:1;
};
#ifdef CONFIG_PM
static int dwc3_omap_suspend(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
memcpy_fromio(omap->context, omap->base, omap->resource_size);
return 0;
}
static int dwc3_omap_resume(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
memcpy_toio(omap->base, omap->context, omap->resource_size);
return 0;
}
static int dwc3_omap_idle(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
u32 reg;
/* stop DMA Engine */
reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
reg &= ~(USBOTGSS_SYSCONFIG_DMADISABLE);
dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
return 0;
}
static UNIVERSAL_DEV_PM_OPS(dwc3_omap_pm_ops, dwc3_omap_suspend,
dwc3_omap_resume, dwc3_omap_idle);
#define DEV_PM_OPS (&dwc3_omap_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
struct dwc3_omap *omap = _omap;
u32 reg;
u32 ctrl;
spin_lock(&omap->lock);
reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
ctrl = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_CTRL);
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
dev_dbg(omap->base, "DMA Disable was Cleared\n");
omap->dma_status = false;
}
if (reg & USBOTGSS_IRQ1_OEVT)
dev_dbg(omap->base, "OTG Event\n");
if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) {
dev_dbg(omap->base, "DRVVBUS Rise\n");
ctrl |= USBOTGSS_UTMI_OTG_CTRL_DRVVBUS;
}
if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) {
dev_dbg(omap->base, "CHRGVBUS Rise\n");
ctrl |= USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS;
}
if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) {
dev_dbg(omap->base, "DISCHRGVBUS Rise\n");
ctrl |= USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS;
}
if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) {
dev_dbg(omap->base, "IDPULLUP Rise\n");
ctrl |= USBOTGSS_UTMI_OTG_CTRL_IDPULLUP;
}
if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) {
dev_dbg(omap->base, "DRVVBUS Fall\n");
ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DRVVBUS;
}
if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) {
dev_dbg(omap->base, "CHRGVBUS Fall\n");
ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS;
}
if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) {
dev_dbg(omap->base, "DISCHRGVBUS Fall\n");
ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS;
}
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) {
dev_dbg(omap->base, "IDPULLUP Fall\n");
ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_IDPULLUP;
}
dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_CTRL, ctrl);
spin_unlock(&omap->lock);
return IRQ_HANDLED;
}
static int __devinit dwc3_omap_probe(struct platform_device *pdev)
{
struct platform_device *dwc3;
struct dwc3_omap *omap;
struct resource *res;
int ret = -ENOMEM;
int irq;
u32 reg;
void __iomem *base;
void *context;
omap = kzalloc(sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(&pdev->dev, "not enough memory\n");
goto err0;
}
platform_set_drvdata(pdev, omap);
irq = platform_get_irq(pdev, 1);
if (irq < 0) {
dev_err(&pdev->dev, "missing IRQ resource\n");
ret = -EINVAL;
goto err1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "missing memory base resource\n");
ret = -EINVAL;
goto err1;
}
base = ioremap_nocache(res->start, resource_size(res));
if (!base) {
dev_err(&pdev->dev, "ioremap failed\n");
goto err1;
}
dwc3 = platform_device_alloc("dwc3-omap", -1);
if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
goto err2;
}
context = kzalloc(resource_size(res), GFP_KERNEL);
if (!context) {
dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n");
goto err3;
}
spin_lock_init(&omap->lock);
dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
dwc3->dev.parent = &pdev->dev;
dwc3->dev.dma_mask = pdev->dev.dma_mask;
dwc3->dev.dma_parms = pdev->dev.dma_parms;
omap->resource_size = resource_size(res);
omap->context = context;
omap->dev = &pdev->dev;
omap->irq = irq;
omap->base = base;
omap->dwc3 = dwc3;
/* check the DMA Status */
reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
ret = request_irq(omap->irq, dwc3_omap_interrupt, 0,
"dwc3-wrapper", omap);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
goto err4;
}
/* enable all IRQs */
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x01);
reg = (USBOTGSS_IRQ1_DMADISABLECLR |
USBOTGSS_IRQ1_OEVT |
USBOTGSS_IRQ1_DRVVBUS_RISE |
USBOTGSS_IRQ1_CHRGVBUS_RISE |
USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
USBOTGSS_IRQ1_IDPULLUP_RISE |
USBOTGSS_IRQ1_DRVVBUS_FALL |
USBOTGSS_IRQ1_CHRGVBUS_FALL |
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
USBOTGSS_IRQ1_IDPULLUP_FALL);
dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n");
goto err5;
}
ret = platform_device_add(dwc3);
if (ret) {
dev_err(&pdev->dev, "failed to register dwc3 device\n");
goto err5;
}
return 0;
err5:
free_irq(omap->irq, omap);
err4:
kfree(omap->context);
err3:
platform_device_put(dwc3);
err2:
iounmap(base);
err1:
kfree(omap);
err0:
return ret;
}
static int __devexit dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
platform_device_unregister(omap->dwc3);
free_irq(omap->irq, omap);
iounmap(omap->base);
kfree(omap->context);
kfree(omap);
return 0;
}
static const struct of_device_id of_dwc3_matach[] = {
{
"ti,dwc3",
},
{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_matach);
static struct platform_driver dwc3_omap_driver = {
.probe = dwc3_omap_probe,
.remove = __devexit_p(dwc3_omap_remove),
.driver = {
.name = "omap-dwc3",
.pm = DEV_PM_OPS,
.of_match_table = of_dwc3_matach,
},
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
static int __devinit dwc3_omap_init(void)
{
return platform_driver_register(&dwc3_omap_driver);
}
module_init(dwc3_omap_init);
static void __exit dwc3_omap_exit(void)
{
platform_driver_unregister(&dwc3_omap_driver);
}
module_exit(dwc3_omap_exit);
/**
* dwc3-pci.c - PCI Specific glue layer
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
#define DWC3_PCI_DEVS_POSSIBLE 32
struct dwc3_pci {
struct device *dev;
struct platform_device *dwc3;
};
static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
static int dwc3_pci_get_device_id(struct dwc3_pci *glue)
{
int id;
again:
id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE);
if (id < DWC3_PCI_DEVS_POSSIBLE) {
int old;
old = test_and_set_bit(id, dwc3_pci_devs);
if (old)
goto again;
} else {
dev_err(glue->dev, "no space for new device\n");
id = -ENOMEM;
}
return 0;
}
static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id)
{
int ret;
if (id < 0)
return;
ret = test_bit(id, dwc3_pci_devs);
WARN(!ret, "Device: %s\nID %d not in use\n",
dev_driver_string(glue->dev), id);
clear_bit(id, dwc3_pci_devs);
}
static int __devinit dwc3_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct resource res[2];
struct platform_device *dwc3;
struct dwc3_pci *glue;
int ret = -ENOMEM;
int devid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pci->dev, "not enough memory\n");
goto err0;
}
glue->dev = &pci->dev;
ret = pci_enable_device(pci);
if (ret) {
dev_err(&pci->dev, "failed to enable pci device\n");
goto err1;
}
pci_set_power_state(pci, PCI_D0);
pci_set_master(pci);
devid = dwc3_pci_get_device_id(glue);
if (devid < 0)
goto err2;
dwc3 = platform_device_alloc("dwc3-pci", devid);
if (!dwc3) {
dev_err(&pci->dev, "couldn't allocate dwc3 device\n");
goto err3;
}
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
res[0].start = pci_resource_start(pci, 0);
res[0].end = pci_resource_end(pci, 0);
res[0].name = "dwc_usb3";
res[0].flags = IORESOURCE_MEM;
res[1].start = pci->irq;
res[1].name = "dwc_usb3";
res[1].flags = IORESOURCE_IRQ;
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(&pci->dev, "couldn't add resources to dwc3 device\n");
goto err4;
}
pci_set_drvdata(pci, glue);
dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask);
dwc3->dev.dma_mask = pci->dev.dma_mask;
dwc3->dev.dma_parms = pci->dev.dma_parms;
dwc3->dev.parent = &pci->dev;
glue->dwc3 = dwc3;
ret = platform_device_add(dwc3);
if (ret) {
dev_err(&pci->dev, "failed to register dwc3 device\n");
goto err4;
}
return 0;
err4:
pci_set_drvdata(pci, NULL);
platform_device_put(dwc3);
err3:
dwc3_pci_put_device_id(glue, devid);
err2:
pci_disable_device(pci);
err1:
kfree(pci);
err0:
return ret;
}
static void __devexit dwc3_pci_remove(struct pci_dev *pci)
{
struct dwc3_pci *glue = pci_get_drvdata(pci);
dwc3_pci_put_device_id(glue, glue->dwc3->id);
platform_device_unregister(glue->dwc3);
pci_set_drvdata(pci, NULL);
pci_disable_device(pci);
kfree(glue);
}
static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
{
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
},
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
static struct pci_driver dwc3_pci_driver = {
.name = "pci-dwc3",
.id_table = dwc3_pci_id_table,
.probe = dwc3_pci_probe,
.remove = __devexit_p(dwc3_pci_remove),
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");
static int __devinit dwc3_pci_init(void)
{
return pci_register_driver(&dwc3_pci_driver);
}
module_init(dwc3_pci_init);
static void __exit dwc3_pci_exit(void)
{
pci_unregister_driver(&dwc3_pci_driver);
}
module_exit(dwc3_pci_exit);
/**
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "gadget.h"
#include "io.h"
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event);
static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
{
switch (state) {
case EP0_UNCONNECTED:
return "Unconnected";
case EP0_IDLE:
return "Idle";
case EP0_IN_DATA_PHASE:
return "IN Data Phase";
case EP0_OUT_DATA_PHASE:
return "OUT Data Phase";
case EP0_IN_WAIT_GADGET:
return "IN Wait Gadget";
case EP0_OUT_WAIT_GADGET:
return "OUT Wait Gadget";
case EP0_IN_WAIT_NRDY:
return "IN Wait NRDY";
case EP0_OUT_WAIT_NRDY:
return "OUT Wait NRDY";
case EP0_IN_STATUS_PHASE:
return "IN Status Phase";
case EP0_OUT_STATUS_PHASE:
return "OUT Status Phase";
case EP0_STALL:
return "Stall";
default:
return "UNKNOWN";
}
}
static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
u32 len)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb_hw *trb_hw;
struct dwc3_trb trb;
struct dwc3_ep *dep;
int ret;
dep = dwc->eps[epnum];
trb_hw = dwc->ep0_trb;
memset(&trb, 0, sizeof(trb));
switch (dwc->ep0state) {
case EP0_IDLE:
trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP;
break;
case EP0_IN_WAIT_NRDY:
case EP0_OUT_WAIT_NRDY:
case EP0_IN_STATUS_PHASE:
case EP0_OUT_STATUS_PHASE:
if (dwc->three_stage_setup)
trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS3;
else
trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS2;
if (dwc->ep0state == EP0_IN_WAIT_NRDY)
dwc->ep0state = EP0_IN_STATUS_PHASE;
else if (dwc->ep0state == EP0_OUT_WAIT_NRDY)
dwc->ep0state = EP0_OUT_STATUS_PHASE;
break;
case EP0_IN_WAIT_GADGET:
dwc->ep0state = EP0_IN_WAIT_NRDY;
return 0;
break;
case EP0_OUT_WAIT_GADGET:
dwc->ep0state = EP0_OUT_WAIT_NRDY;
return 0;
break;
case EP0_IN_DATA_PHASE:
case EP0_OUT_DATA_PHASE:
trb.trbctl = DWC3_TRBCTL_CONTROL_DATA;
break;
default:
dev_err(dwc->dev, "%s() can't in state %d\n", __func__,
dwc->ep0state);
return -EINVAL;
}
trb.bplh = buf_dma;
trb.length = len;
trb.hwo = 1;
trb.lst = 1;
trb.ioc = 1;
trb.isp_imi = 1;
dwc3_trb_to_hw(&trb, trb_hw);
memset(&params, 0, sizeof(params));
params.param0.depstrtxfer.transfer_desc_addr_high =
upper_32_bits(dwc->ep0_trb_addr);
params.param1.depstrtxfer.transfer_desc_addr_low =
lower_32_bits(dwc->ep0_trb_addr);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, &params);
if (ret < 0) {
dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
return ret;
}
dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc,
dep->number);
return 0;
}
static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
int ret;
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
req->epnum = dep->number;
list_add_tail(&req->list, &dep->request_list);
dwc3_map_buffer_to_dma(req);
ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
req->request.length);
if (ret < 0) {
list_del(&req->list);
dwc3_unmap_buffer_from_dma(req);
}
return ret;
}
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
struct dwc3_request *req = to_dwc3_request(request);
struct dwc3_ep *dep = to_dwc3_ep(ep);
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int ret;
switch (dwc->ep0state) {
case EP0_IN_DATA_PHASE:
case EP0_IN_WAIT_GADGET:
case EP0_IN_WAIT_NRDY:
case EP0_IN_STATUS_PHASE:
dep = dwc->eps[1];
break;
case EP0_OUT_DATA_PHASE:
case EP0_OUT_WAIT_GADGET:
case EP0_OUT_WAIT_NRDY:
case EP0_OUT_STATUS_PHASE:
dep = dwc->eps[0];
break;
default:
return -EINVAL;
}
spin_lock_irqsave(&dwc->lock, flags);
if (!dep->desc) {
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
request, dep->name);
ret = -ESHUTDOWN;
goto out;
}
/* we share one TRB for ep0/1 */
if (!list_empty(&dwc->eps[0]->request_list) ||
!list_empty(&dwc->eps[1]->request_list) ||
dwc->ep0_status_pending) {
ret = -EBUSY;
goto out;
}
dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n",
request, dep->name, request->length,
dwc3_ep0_state_string(dwc->ep0state));
ret = __dwc3_gadget_ep0_queue(dep, req);
out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
}
static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
/* stall is always issued on EP0 */
__dwc3_gadget_ep_set_halt(dwc->eps[0], 1);
dwc->eps[0]->flags &= ~DWC3_EP_STALL;
dwc->ep0state = EP0_IDLE;
dwc3_ep0_out_start(dwc);
}
void dwc3_ep0_out_start(struct dwc3 *dwc)
{
struct dwc3_ep *dep;
int ret;
dep = dwc->eps[0];
ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8);
WARN_ON(ret < 0);
}
/*
* Send a zero length packet for the status phase of the control transfer
*/
static void dwc3_ep0_do_setup_status(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_ep *dep;
int ret;
u32 epnum;
epnum = event->endpoint_number;
dep = dwc->eps[epnum];
if (epnum)
dwc->ep0state = EP0_IN_STATUS_PHASE;
else
dwc->ep0state = EP0_OUT_STATUS_PHASE;
/*
* Not sure Why I need a buffer for a zero transfer. Maybe the
* HW reacts strange on a NULL pointer
*/
ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0);
if (ret) {
dev_dbg(dwc->dev, "failed to start transfer, stalling\n");
dwc3_ep0_stall_and_restart(dwc);
}
}
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
{
struct dwc3_ep *dep;
u32 windex = le16_to_cpu(wIndex_le);
u32 epnum;
epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1;
if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
epnum |= 1;
dep = dwc->eps[epnum];
if (dep->flags & DWC3_EP_ENABLED)
return dep;
return NULL;
}
static void dwc3_ep0_send_status_response(struct dwc3 *dwc)
{
u32 epnum;
if (dwc->ep0state == EP0_IN_DATA_PHASE)
epnum = 1;
else
epnum = 0;
dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr,
dwc->ep0_usb_req.length);
dwc->ep0_status_pending = 1;
}
/*
* ch 9.4.5
*/
static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
u32 recip;
u16 usb_status = 0;
__le16 *response_pkt;
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
/*
* We are self-powered. U1/U2/LTM will be set later
* once we handle this states. RemoteWakeup is 0 on SS
*/
usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
break;
case USB_RECIP_INTERFACE:
/*
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
break;
case USB_RECIP_ENDPOINT:
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
if (!dep)
return -EINVAL;
if (dep->flags & DWC3_EP_STALL)
usb_status = 1 << USB_ENDPOINT_HALT;
break;
default:
return -EINVAL;
};
response_pkt = (__le16 *) dwc->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
dwc->ep0_usb_req.length = sizeof(*response_pkt);
dwc3_ep0_send_status_response(dwc);
return 0;
}
static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
struct usb_ctrlrequest *ctrl, int set)
{
struct dwc3_ep *dep;
u32 recip;
u32 wValue;
u32 wIndex;
u32 reg;
int ret;
u32 mode;
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
recip = ctrl->bRequestType & USB_RECIP_MASK;
switch (recip) {
case USB_RECIP_DEVICE:
/*
* 9.4.1 says only only for SS, in AddressState only for
* default control pipe
*/
switch (wValue) {
case USB_DEVICE_U1_ENABLE:
case USB_DEVICE_U2_ENABLE:
case USB_DEVICE_LTM_ENABLE:
if (dwc->dev_state != DWC3_CONFIGURED_STATE)
return -EINVAL;
if (dwc->speed != DWC3_DSTS_SUPERSPEED)
return -EINVAL;
}
/* XXX add U[12] & LTM */
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
break;
case USB_DEVICE_U1_ENABLE:
break;
case USB_DEVICE_U2_ENABLE:
break;
case USB_DEVICE_LTM_ENABLE:
break;
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
return -EINVAL;
if (!set)
return -EINVAL;
mode = wIndex >> 8;
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
case TEST_FORCE_EN:
reg |= mode << 1;
break;
default:
return -EINVAL;
}
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
break;
default:
return -EINVAL;
}
break;
case USB_RECIP_INTERFACE:
switch (wValue) {
case USB_INTRF_FUNC_SUSPEND:
if (wIndex & USB_INTRF_FUNC_SUSPEND_LP)
/* XXX enable Low power suspend */
;
if (wIndex & USB_INTRF_FUNC_SUSPEND_RW)
/* XXX enable remote wakeup */
;
break;
default:
return -EINVAL;
}
break;
case USB_RECIP_ENDPOINT:
switch (wValue) {
case USB_ENDPOINT_HALT:
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
if (!dep)
return -EINVAL;
ret = __dwc3_gadget_ep_set_halt(dep, set);
if (ret)
return -EINVAL;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
};
dwc->ep0state = EP0_IN_WAIT_NRDY;
return 0;
}
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
int ret = 0;
u32 addr;
u32 reg;
addr = le16_to_cpu(ctrl->wValue);
if (addr > 127)
return -EINVAL;
switch (dwc->dev_state) {
case DWC3_DEFAULT_STATE:
case DWC3_ADDRESS_STATE:
/*
* Not sure if we should program DevAddr now or later
*/
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
reg |= DWC3_DCFG_DEVADDR(addr);
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
dwc->dev_state = DWC3_ADDRESS_STATE;
else
dwc->dev_state = DWC3_DEFAULT_STATE;
break;
case DWC3_CONFIGURED_STATE:
ret = -EINVAL;
break;
}
dwc->ep0state = EP0_IN_WAIT_NRDY;
return ret;
}
static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
int ret;
spin_unlock(&dwc->lock);
ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
spin_lock(&dwc->lock);
return ret;
}
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
u32 cfg;
int ret;
cfg = le16_to_cpu(ctrl->wValue);
switch (dwc->dev_state) {
case DWC3_DEFAULT_STATE:
return -EINVAL;
break;
case DWC3_ADDRESS_STATE:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (!ret && cfg)
dwc->dev_state = DWC3_CONFIGURED_STATE;
break;
case DWC3_CONFIGURED_STATE:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg)
dwc->dev_state = DWC3_ADDRESS_STATE;
break;
}
return 0;
}
static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
int ret;
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
default:
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
};
return ret;
}
static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct usb_ctrlrequest *ctrl = dwc->ctrl_req;
int ret;
u32 len;
if (!dwc->gadget_driver)
goto err;
len = le16_to_cpu(ctrl->wLength);
if (!len) {
dwc->ep0state = EP0_IN_WAIT_GADGET;
dwc->three_stage_setup = 0;
} else {
dwc->three_stage_setup = 1;
if (ctrl->bRequestType & USB_DIR_IN)
dwc->ep0state = EP0_IN_DATA_PHASE;
else
dwc->ep0state = EP0_OUT_DATA_PHASE;
}
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
ret = dwc3_ep0_std_request(dwc, ctrl);
else
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (ret >= 0)
return;
err:
dwc3_ep0_stall_and_restart(dwc);
}
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_request *r = NULL;
struct usb_request *ur;
struct dwc3_trb trb;
struct dwc3_ep *dep;
u32 transfered;
u8 epnum;
epnum = event->endpoint_number;
dep = dwc->eps[epnum];
if (!dwc->ep0_status_pending) {
r = next_request(&dep->request_list);
ur = &r->request;
} else {
ur = &dwc->ep0_usb_req;
dwc->ep0_status_pending = 0;
}
dwc3_trb_to_nat(dwc->ep0_trb, &trb);
transfered = ur->length - trb.length;
ur->actual += transfered;
if ((epnum & 1) && ur->actual < ur->length) {
/* for some reason we did not get everything out */
dwc3_ep0_stall_and_restart(dwc);
dwc3_gadget_giveback(dep, r, -ECONNRESET);
} else {
/*
* handle the case where we have to send a zero packet. This
* seems to be case when req.length > maxpacket. Could it be?
*/
/* The transfer is complete, wait for HOST */
if (epnum & 1)
dwc->ep0state = EP0_IN_WAIT_NRDY;
else
dwc->ep0state = EP0_OUT_WAIT_NRDY;
if (r)
dwc3_gadget_giveback(dep, r, 0);
}
}
static void dwc3_ep0_complete_req(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_request *r;
struct dwc3_ep *dep;
u8 epnum;
epnum = event->endpoint_number;
dep = dwc->eps[epnum];
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
dwc3_gadget_giveback(dep, r, 0);
}
dwc->ep0state = EP0_IDLE;
dwc3_ep0_out_start(dwc);
}
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
switch (dwc->ep0state) {
case EP0_IDLE:
dwc3_ep0_inspect_setup(dwc, event);
break;
case EP0_IN_DATA_PHASE:
case EP0_OUT_DATA_PHASE:
dwc3_ep0_complete_data(dwc, event);
break;
case EP0_IN_STATUS_PHASE:
case EP0_OUT_STATUS_PHASE:
dwc3_ep0_complete_req(dwc, event);
break;
case EP0_IN_WAIT_NRDY:
case EP0_OUT_WAIT_NRDY:
case EP0_IN_WAIT_GADGET:
case EP0_OUT_WAIT_GADGET:
case EP0_UNCONNECTED:
case EP0_STALL:
break;
}
}
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
switch (dwc->ep0state) {
case EP0_IN_WAIT_GADGET:
dwc->ep0state = EP0_IN_WAIT_NRDY;
break;
case EP0_OUT_WAIT_GADGET:
dwc->ep0state = EP0_OUT_WAIT_NRDY;
break;
case EP0_IN_WAIT_NRDY:
case EP0_OUT_WAIT_NRDY:
dwc3_ep0_do_setup_status(dwc, event);
break;
case EP0_IDLE:
case EP0_IN_STATUS_PHASE:
case EP0_OUT_STATUS_PHASE:
case EP0_IN_DATA_PHASE:
case EP0_OUT_DATA_PHASE:
case EP0_UNCONNECTED:
case EP0_STALL:
break;
}
}
void dwc3_ep0_interrupt(struct dwc3 *dwc,
const const struct dwc3_event_depevt *event)
{
u8 epnum = event->endpoint_number;
dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n",
dwc3_ep_event_string(event->endpoint_event),
epnum, (epnum & 1) ? "in" : "out",
dwc3_ep0_state_string(dwc->ep0state));
switch (event->endpoint_event) {
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_ep0_xfer_complete(dwc, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
dwc3_ep0_xfernotready(dwc, event);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
case DWC3_DEPEVT_RXTXFIFOEVT:
case DWC3_DEPEVT_STREAMEVT:
case DWC3_DEPEVT_EPCMDCMPLT:
break;
}
}
此差异已折叠。
/**
* gadget.h - DesignWare USB3 DRD Gadget Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS 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 __DRIVERS_USB_DWC3_GADGET_H
#define __DRIVERS_USB_DWC3_GADGET_H
#include <linux/list.h>
#include <linux/usb/gadget.h>
#include "io.h"
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/**
* struct dwc3_gadget_ep_depcfg_param1 - DEPCMDPAR0 for DEPCFG command
* @interrupt_number: self-explanatory
* @reserved7_5: set to zero
* @xfer_complete_enable: event generated when transfer completed
* @xfer_in_progress_enable: event generated when transfer in progress
* @xfer_not_ready_enable: event generated when transfer not read
* @fifo_error_enable: generates events when FIFO Underrun (IN eps)
* or FIFO Overrun (OUT) eps
* @reserved_12: set to zero
* @stream_event_enable: event generated on stream
* @reserved14_15: set to zero
* @binterval_m1: bInterval minus 1
* @stream_capable: this EP is capable of handling streams
* @ep_number: self-explanatory
* @bulk_based: Set to ‘1’ if this isochronous endpoint represents a bulk
* data stream that ignores the relationship of bus time to the
* intervals programmed in TRBs.
* @fifo_based: Set to ‘1’ if this isochronous endpoint represents a
* FIFO-based data stream where TRBs have fixed values and are never
* written back by the core.
*/
struct dwc3_gadget_ep_depcfg_param1 {
u32 interrupt_number:5;
u32 reserved7_5:3; /* set to zero */
u32 xfer_complete_enable:1;
u32 xfer_in_progress_enable:1;
u32 xfer_not_ready_enable:1;
u32 fifo_error_enable:1; /* IN-underrun, OUT-overrun */
u32 reserved12:1; /* set to zero */
u32 stream_event_enable:1;
u32 reserved14_15:2;
u32 binterval_m1:8; /* bInterval minus 1 */
u32 stream_capable:1;
u32 ep_number:5;
u32 bulk_based:1;
u32 fifo_based:1;
} __packed;
/**
* struct dwc3_gadget_ep_depcfg_param0 - Parameter 0 for DEPCFG
* @reserved0: set to zero
* @ep_type: Endpoint Type (control, bulk, iso, interrupt)
* @max_packet_size: max packet size in bytes
* @reserved16_14: set to zero
* @fifo_number: self-explanatory
* @burst_size: burst size minus 1
* @data_sequence_number: Must be 0 when an endpoint is initially configured
* May be non-zero when an endpoint is configured after a power transition
* that requires a save/restore.
* @ignore_sequence_number: Set to ‘1’ to avoid resetting the sequence
* number. This setting is used by software to modify the DEPEVTEN
* event enable bits without modifying other endpoint settings.
*/
struct dwc3_gadget_ep_depcfg_param0 {
u32 reserved0:1;
u32 ep_type:2;
u32 max_packet_size:11;
u32 reserved16_14:3;
u32 fifo_number:5;
u32 burst_size:4;
u32 data_sequence_number:5;
u32 ignore_sequence_number:1;
} __packed;
/**
* struct dwc3_gadget_ep_depxfercfg_param0 - Parameter 0 of DEPXFERCFG
* @number_xfer_resources: Defines the number of Transfer Resources allocated
* to this endpoint. This field must be set to 1.
* @reserved16_31: set to zero;
*/
struct dwc3_gadget_ep_depxfercfg_param0 {
u32 number_xfer_resources:16;
u32 reserved16_31:16;
} __packed;
/**
* struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER
* @transfer_desc_addr_low: Indicates the lower 32 bits of the external
* memory's start address for the transfer descriptor. Because TRBs
* must be aligned to a 16-byte boundary, the lower 4 bits of this
* address must be 0.
*/
struct dwc3_gadget_ep_depstrtxfer_param1 {
u32 transfer_desc_addr_low;
} __packed;
/**
* struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER
* @transfer_desc_addr_high: Indicates the higher 32 bits of the external
* memory’s start address for the transfer descriptor.
*/
struct dwc3_gadget_ep_depstrtxfer_param0 {
u32 transfer_desc_addr_high;
} __packed;
struct dwc3_gadget_ep_cmd_params {
union {
u32 raw;
} param2;
union {
u32 raw;
struct dwc3_gadget_ep_depcfg_param1 depcfg;
struct dwc3_gadget_ep_depstrtxfer_param1 depstrtxfer;
} param1;
union {
u32 raw;
struct dwc3_gadget_ep_depcfg_param0 depcfg;
struct dwc3_gadget_ep_depxfercfg_param0 depxfercfg;
struct dwc3_gadget_ep_depstrtxfer_param0 depstrtxfer;
} param0;
} __packed;
/* -------------------------------------------------------------------------- */
struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
u8 epnum;
struct dwc3_trb_hw *trb;
dma_addr_t trb_dma;
unsigned direction:1;
unsigned mapped:1;
unsigned queued:1;
};
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
static inline struct dwc3_request *next_request(struct list_head *list)
{
if (list_empty(list))
return NULL;
return list_first_entry(list, struct dwc3_request, list);
}
static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
{
struct dwc3_ep *dep = req->dep;
req->queued = true;
list_move_tail(&req->list, &dep->req_queued);
}
#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE)
int dwc3_gadget_init(struct dwc3 *dwc);
void dwc3_gadget_exit(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; }
static inline void dwc3_gadget_exit(struct dwc3 *dwc) { }
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
{
return 0;
}
#endif
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status);
void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event);
void dwc3_ep0_out_start(struct dwc3 *dwc);
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags);
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
void dwc3_map_buffer_to_dma(struct dwc3_request *req);
void dwc3_unmap_buffer_from_dma(struct dwc3_request *req);
/**
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
* @dwc: DesignWare USB3 Pointer
* @number: DWC endpoint number
*
* Caller should take care of locking
*/
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
{
u32 res_id;
res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
/**
* dwc3_gadget_event_string - returns event name
* @event: the event code
*/
static inline const char *dwc3_gadget_event_string(u8 event)
{
switch (event) {
case DWC3_DEVICE_EVENT_DISCONNECT:
return "Disconnect";
case DWC3_DEVICE_EVENT_RESET:
return "Reset";
case DWC3_DEVICE_EVENT_CONNECT_DONE:
return "Connection Done";
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
return "Link Status Change";
case DWC3_DEVICE_EVENT_WAKEUP:
return "WakeUp";
case DWC3_DEVICE_EVENT_EOPF:
return "End-Of-Frame";
case DWC3_DEVICE_EVENT_SOF:
return "Start-Of-Frame";
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
return "Erratic Error";
case DWC3_DEVICE_EVENT_CMD_CMPL:
return "Command Complete";
case DWC3_DEVICE_EVENT_OVERFLOW:
return "Overflow";
}
return "UNKNOWN";
}
/**
* dwc3_ep_event_string - returns event name
* @event: then event code
*/
static inline const char *dwc3_ep_event_string(u8 event)
{
switch (event) {
case DWC3_DEPEVT_XFERCOMPLETE:
return "Transfer Complete";
case DWC3_DEPEVT_XFERINPROGRESS:
return "Transfer In-Progress";
case DWC3_DEPEVT_XFERNOTREADY:
return "Transfer Not Ready";
case DWC3_DEPEVT_RXTXFIFOEVT:
return "FIFO";
case DWC3_DEPEVT_STREAMEVT:
return "Stream";
case DWC3_DEPEVT_EPCMDCMPLT:
return "Endpoint Command Complete";
}
return "UNKNOWN";
}
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
/**
* io.h - DesignWare USB3 DRD IO Header
*
* Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
* All rights reserved.
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2, as published by the Free
* Software Foundation.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS 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 __DRIVERS_USB_DWC3_IO_H
#define __DRIVERS_USB_DWC3_IO_H
#include <asm/io.h>
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
return readl(base + offset);
}
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
{
writel(value, base + offset);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
......@@ -255,12 +255,11 @@ config USB_S3C_HSOTG
integrated into the S3C64XX series SoC.
config USB_IMX
tristate "Freescale IMX USB Peripheral Controller"
depends on ARCH_MX1
tristate "Freescale i.MX1 USB Peripheral Controller"
depends on ARCH_MXC
help
Freescale's IMX series include an integrated full speed
USB 1.1 device controller. The controller in the IMX series
is register-compatible.
Freescale's i.MX1 includes an integrated full speed
USB 1.1 device controller.
It has Six fixed-function endpoints, as well as endpoint
zero (for control transfers).
......@@ -303,6 +302,18 @@ config USB_PXA_U2O
PXA9xx Processor series include a high speed USB2.0 device
controller, which support high speed and full speed USB peripheral.
config USB_GADGET_DWC3
tristate "DesignWare USB3.0 (DRD) Controller"
depends on USB_DWC3
select USB_GADGET_DUALSPEED
select USB_GADGET_SUPERSPEED
help
DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller
which can be configured for peripheral-only, host-only, hub-only
and Dual-Role operation. This Controller was first integrated into
the OMAP5 series of processors. More information about the OMAP5
version of this controller, refer to http://www.ti.com/omap5.
#
# Controllers available in both integrated and discrete versions
#
......
......@@ -354,7 +354,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc)
writel(tmp, &dev->ep[ep->num].regs->ctl);
/* set max packet size */
maxpacket = le16_to_cpu(desc->wMaxPacketSize);
maxpacket = usb_endpoint_maxp(desc);
tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE);
ep->ep.maxpacket = maxpacket;
......
......@@ -487,7 +487,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
|| !desc || ep->desc
|| _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0
|| (maxpacket = usb_endpoint_maxp(desc)) == 0
|| maxpacket > ep->maxpacket) {
DBG("bad ep or descriptor\n");
return -EINVAL;
......
......@@ -527,7 +527,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc);
maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
maxpacket = usb_endpoint_maxp(desc) & 0x7ff;
if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index)
|| ep->index == 0
......@@ -571,7 +571,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
* Bits 11:12 specify number of _additional_
* transactions per microframe.
*/
nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1;
nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1;
if (nr_trans > 3)
return -EINVAL;
......
......@@ -2101,7 +2101,7 @@ static int ep_enable(struct usb_ep *ep,
mEp->num = usb_endpoint_num(desc);
mEp->type = usb_endpoint_type(desc);
mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize);
mEp->ep.maxpacket = usb_endpoint_maxp(desc);
dbg_event(_usb_addr(mEp), "ENABLE", 0);
......
......@@ -164,7 +164,7 @@ int config_ep_by_speed(struct usb_gadget *g,
ep_found:
/* commit results */
_ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize);
_ep->maxpacket = usb_endpoint_maxp(chosen_desc);
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
......
......@@ -439,7 +439,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
* maximum packet size.
* For SS devices the wMaxPacketSize is limited by 1024.
*/
max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff;
max = usb_endpoint_maxp(desc) & 0x7ff;
/* drivers must not request bad settings, since lower levels
* (hardware or its drivers) may not check. some endpoints
......@@ -1277,7 +1277,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
int tmp;
/* high bandwidth mode */
tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
tmp = usb_endpoint_maxp(ep->desc);
tmp = (tmp >> 11) & 0x03;
tmp *= 8 /* applies to entire frame */;
limit += limit * tmp;
......
......@@ -158,7 +158,7 @@ ep_matches (
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize);
max = 0x7ff & usb_endpoint_maxp(desc);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high/super speed */
......
......@@ -2401,8 +2401,7 @@ static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
goto reset;
fsg->bulk_out->driver_data = common;
fsg->bulk_out_enabled = 1;
common->bulk_out_maxpacket =
le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize);
common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
/* Allocate the requests */
......
......@@ -2801,7 +2801,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting)
if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
goto reset;
fsg->bulk_out_enabled = 1;
fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
fsg->bulk_out_maxpacket = usb_endpoint_maxp(d);
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
if (transport_is_cbi()) {
......
......@@ -540,7 +540,7 @@ static int qe_ep_init(struct qe_udc *udc,
int reval = 0;
u16 max = 0;
max = le16_to_cpu(desc->wMaxPacketSize);
max = usb_endpoint_maxp(desc);
/* check the max package size validate for this endpoint */
/* Refer to USB2.0 spec table 9-13,
......
......@@ -559,7 +559,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
max = le16_to_cpu(desc->wMaxPacketSize);
max = usb_endpoint_maxp(desc);
/* Disable automatic zlp generation. Driver is responsible to indicate
* explicitly through req->req.zero. This is needed to enable multi-td
......
......@@ -220,7 +220,7 @@ static int config_ep(struct fusb300_ep *ep,
info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
info.maxpacket = usb_endpoint_maxp(desc);
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
if ((info.type == USB_ENDPOINT_XFER_INT) ||
......
......@@ -31,6 +31,7 @@
#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
......@@ -115,6 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x30;
else if (gadget_is_net2272(gadget))
return 0x31;
else if (gadget_is_dwc3(gadget))
return 0x32;
return -ENOENT;
}
......
......@@ -689,7 +689,7 @@ static int imx_ep_enable(struct usb_ep *usb_ep,
return -EINVAL;
}
if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) {
if (imx_ep->fifosize < usb_endpoint_maxp(desc)) {
D_ERR(imx_usb->dev,
"<%s> bad %s maxpacket\n", __func__, usb_ep->name);
return -ERANGE;
......
......@@ -283,7 +283,7 @@ static int langwell_ep_enable(struct usb_ep *_ep,
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max = le16_to_cpu(desc->wMaxPacketSize);
max = usb_endpoint_maxp(desc);
/*
* disable HW zero length termination select
......
......@@ -370,7 +370,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
ep->pipectr = get_pipectr_addr(pipenum);
ep->pipenum = pipenum;
ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
ep->ep.maxpacket = usb_endpoint_maxp(desc);
m66592->pipenum2ep[pipenum] = ep;
m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
INIT_LIST_HEAD(&ep->queue);
......@@ -447,7 +447,7 @@ static int alloc_pipe_config(struct m66592_ep *ep,
ep->type = info.type;
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
info.dir_in = 1;
......
......@@ -493,7 +493,7 @@ static int mv_ep_enable(struct usb_ep *_ep,
return -ESHUTDOWN;
direction = ep_dir(ep);
max = le16_to_cpu(desc->wMaxPacketSize);
max = usb_endpoint_maxp(desc);
/*
* disable HW zero length termination select
......
......@@ -204,7 +204,7 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
max = usb_endpoint_maxp(desc) & 0x1fff;
spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7fff;
......
......@@ -169,7 +169,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
return -EDOM;
/* sanity check ep-e/ep-f since their fifos are small */
max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
max = usb_endpoint_maxp (desc) & 0x1fff;
if (ep->num > 4 && max > 64)
return -ERANGE;
......@@ -1640,7 +1640,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
default:
val = "iso"; break;
}; val; }),
le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
usb_endpoint_maxp (d) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
);
} else /* ep0 should only have one transfer queued */
......
......@@ -166,15 +166,14 @@ static int omap_ep_enable(struct usb_ep *_ep,
if (!_ep || !desc || ep->desc
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
|| ep->maxpacket < le16_to_cpu
(desc->wMaxPacketSize)) {
|| ep->maxpacket < usb_endpoint_maxp(desc)) {
DBG("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
maxp = le16_to_cpu (desc->wMaxPacketSize);
maxp = usb_endpoint_maxp(desc);
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& maxp != ep->maxpacket)
|| le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket
|| usb_endpoint_maxp(desc) > ep->maxpacket
|| !desc->wMaxPacketSize) {
DBG("%s, bad %s maxpacket\n", __func__, _ep->name);
return -ERANGE;
......
......@@ -947,7 +947,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep,
else
buff_size = UDC_EPOUT_BUFF_SIZE;
pch_udc_ep_set_bufsz(ep, buff_size, ep->in);
pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize));
pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc));
pch_udc_ep_set_nak(ep);
pch_udc_ep_fifo_flush(ep, ep->in);
/* Configure the endpoint */
......@@ -957,7 +957,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep,
(cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) |
(cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) |
(cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) |
le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT;
usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT;
if (ep->in)
pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num));
......@@ -1466,7 +1466,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
ep->desc = desc;
ep->halted = 0;
pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
ep->ep.maxpacket = usb_endpoint_maxp(desc);
pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
spin_unlock_irqrestore(&dev->lock, iflags);
return 0;
......
......@@ -232,8 +232,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
|| ep->fifo_size < le16_to_cpu
(desc->wMaxPacketSize)) {
|| ep->fifo_size < usb_endpoint_maxp (desc)) {
DMSG("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
......@@ -248,7 +247,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
/* hardware _could_ do smaller, but driver doesn't */
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& le16_to_cpu (desc->wMaxPacketSize)
&& usb_endpoint_maxp (desc)
!= BULK_FIFO_SIZE)
|| !desc->wMaxPacketSize) {
DMSG("%s, bad %s maxpacket\n", __func__, _ep->name);
......@@ -264,7 +263,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep,
ep->desc = desc;
ep->stopped = 0;
ep->pio_irqs = 0;
ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
ep->ep.maxpacket = usb_endpoint_maxp (desc);
/* flush fifo (mostly for OUT buffers) */
pxa25x_ep_fifo_flush (_ep);
......@@ -401,7 +400,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
{
unsigned max;
max = le16_to_cpu(ep->desc->wMaxPacketSize);
max = usb_endpoint_maxp(ep->desc);
do {
unsigned count;
int is_last, is_short;
......@@ -671,8 +670,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
* we can report per-packet status. that also helps with dma.
*/
if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
&& req->req.length > le16_to_cpu
(ep->desc->wMaxPacketSize)))
&& req->req.length > usb_endpoint_maxp (ep->desc)))
return -EMSGSIZE;
DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
......@@ -1105,7 +1103,7 @@ udc_seq_show(struct seq_file *m, void *_d)
tmp = *dev->ep [i].reg_udccs;
seq_printf(m,
"%s max %d %s udccs %02x irqs %lu\n",
ep->ep.name, le16_to_cpu(desc->wMaxPacketSize),
ep->ep.name, usb_endpoint_maxp(desc),
"pio", tmp, ep->pio_irqs);
/* TODO translate all five groups of udccs bits! */
......
......@@ -1439,7 +1439,7 @@ static int pxa_ep_enable(struct usb_ep *_ep,
return -EINVAL;
}
if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
if (ep->fifo_size < usb_endpoint_maxp(desc)) {
ep_err(ep, "bad maxpacket\n");
return -ERANGE;
}
......
......@@ -341,7 +341,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597,
ep->pipectr = get_pipectr_addr(pipenum);
ep->pipenum = pipenum;
ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
ep->ep.maxpacket = usb_endpoint_maxp(desc);
r8a66597->pipenum2ep[pipenum] = ep;
r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK]
= ep;
......@@ -420,7 +420,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep,
ep->type = info.type;
info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
info.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
info.maxpacket = usb_endpoint_maxp(desc);
info.interval = desc->bInterval;
if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
info.dir_in = 1;
......
......@@ -2297,7 +2297,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
return -EINVAL;
}
mps = le16_to_cpu(desc->wMaxPacketSize);
mps = usb_endpoint_maxp(desc);
/* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */
......
......@@ -26,6 +26,7 @@
#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/prefetch.h>
#include <mach/regs-s3c2443-clock.h>
......@@ -137,6 +138,7 @@ struct s3c_hsudc {
struct usb_gadget_driver *driver;
struct device *dev;
struct s3c24xx_hsudc_platdata *pd;
struct otg_transceiver *transceiver;
spinlock_t lock;
void __iomem *regs;
struct resource *mem_rsrc;
......@@ -759,11 +761,11 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
if (!_ep || !desc || hsep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| hsep->bEndpointAddress != desc->bEndpointAddress
|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
|| ep_maxpacket(hsep) < usb_endpoint_maxp(desc))
return -EINVAL;
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
&& usb_endpoint_maxp(desc) != ep_maxpacket(hsep))
|| !desc->wMaxPacketSize)
return -ERANGE;
......@@ -779,7 +781,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
hsep->stopped = hsep->wedge = 0;
hsep->desc = desc;
hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
hsep->ep.maxpacket = usb_endpoint_maxp(desc);
s3c_hsudc_set_halt(_ep, 0);
__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
......@@ -1171,6 +1173,22 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver,
return ret;
}
/* connect to bus through transceiver */
if (hsudc->transceiver) {
ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget);
if (ret) {
dev_err(hsudc->dev, "%s: can't bind to transceiver\n",
hsudc->gadget.name);
driver->unbind(&hsudc->gadget);
device_del(&hsudc->gadget.dev);
hsudc->driver = NULL;
hsudc->gadget.dev.driver = NULL;
return ret;
}
}
enable_irq(hsudc->irq);
dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
......@@ -1201,6 +1219,9 @@ static int s3c_hsudc_stop(struct usb_gadget_driver *driver)
s3c_hsudc_stop_activity(hsudc, driver);
spin_unlock_irqrestore(&hsudc->lock, flags);
if (hsudc->transceiver)
(void) otg_set_peripheral(hsudc->transceiver, NULL);
driver->unbind(&hsudc->gadget);
device_del(&hsudc->gadget.dev);
disable_irq(hsudc->irq);
......@@ -1247,6 +1268,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
hsudc->dev = dev;
hsudc->pd = pdev->dev.platform_data;
hsudc->transceiver = otg_get_transceiver();
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "unable to obtain driver resource data\n");
......@@ -1269,19 +1292,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
goto err_remap;
}
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
goto err_irq;
}
hsudc->irq = ret;
ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
if (ret < 0) {
dev_err(dev, "irq request failed\n");
goto err_irq;
}
spin_lock_init(&hsudc->lock);
device_initialize(&hsudc->gadget.dev);
......@@ -1299,6 +1309,19 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
s3c_hsudc_setup_ep(hsudc);
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
goto err_irq;
}
hsudc->irq = ret;
ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
if (ret < 0) {
dev_err(dev, "irq request failed\n");
goto err_irq;
}
hsudc->uclk = clk_get(&pdev->dev, "usb-device");
if (IS_ERR(hsudc->uclk)) {
dev_err(dev, "failed to find usb-device clock source\n");
......
......@@ -1082,7 +1082,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
max = usb_endpoint_maxp(desc) & 0x1fff;
local_irq_save (flags);
_ep->maxpacket = max & 0x7ff;
......
......@@ -544,11 +544,11 @@ config USB_HWA_HCD
will be called "hwa-hc".
config USB_IMX21_HCD
tristate "iMX21 HCD support"
depends on USB && ARM && MACH_MX21
tristate "i.MX21 HCD support"
depends on USB && ARM && ARCH_MXC
help
This driver enables support for the on-chip USB host in the
iMX21 processor.
i.MX21 processor.
To compile this driver as a module, choose M here: the
module will be called "imx21-hcd".
......@@ -578,3 +578,10 @@ config USB_OCTEON_OHCI
config USB_OCTEON2_COMMON
bool
default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI
config USB_PXA168_EHCI
bool "Marvell PXA168 on-chip EHCI HCD support"
depends on USB_EHCI_HCD && ARCH_MMP
help
Enable support for Marvell PXA168 SoC's on-chip EHCI
host controller
......@@ -293,7 +293,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
......
......@@ -697,6 +697,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
}
#undef DBG_SCHED_LIMIT
static const char *rh_state_string(struct ehci_hcd *ehci)
{
switch (ehci->rh_state) {
case EHCI_RH_HALTED:
return "halted";
case EHCI_RH_SUSPENDED:
return "suspended";
case EHCI_RH_RUNNING:
return "running";
}
return "?";
}
static ssize_t fill_registers_buffer(struct debug_buffer *buf)
{
struct usb_hcd *hcd;
......@@ -730,11 +743,11 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
temp = scnprintf (next, size,
"bus %s, device %s\n"
"%s\n"
"EHCI %x.%02x, hcd state %d\n",
"EHCI %x.%02x, rh state %s\n",
hcd->self.controller->bus->name,
dev_name(hcd->self.controller),
hcd->product_desc,
i >> 8, i & 0x0ff, hcd->state);
i >> 8, i & 0x0ff, rh_state_string(ehci));
size -= temp;
next += temp;
......
......@@ -392,7 +392,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
dev_dbg(dev, "suspending...\n");
hcd->state = HC_STATE_SUSPENDED;
ehci->rh_state = EHCI_RH_SUSPENDED;
dev->power.power_state = PMSG_SUSPEND;
/* ignore non-host interrupts */
......@@ -481,7 +481,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
hcd->state = HC_STATE_RUNNING;
ehci->rh_state = EHCI_RH_RUNNING;
dev->power.power_state = PMSG_ON;
tmp = ehci_readl(ehci, &ehci->regs->command);
......
......@@ -238,7 +238,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
error = handshake(ehci, ptr, mask, done, usec);
if (error) {
ehci_halt(ehci);
ehci_to_hcd(ehci)->state = HC_STATE_HALT;
ehci->rh_state = EHCI_RH_HALTED;
ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
ptr, mask, done, error);
}
......@@ -278,7 +278,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
command |= CMD_RESET;
dbg_cmd (ehci, "reset", command);
ehci_writel(ehci, command, &ehci->regs->command);
ehci_to_hcd(ehci)->state = HC_STATE_HALT;
ehci->rh_state = EHCI_RH_HALTED;
ehci->next_statechange = jiffies;
retval = handshake (ehci, &ehci->regs->command,
CMD_RESET, 0, 250 * 1000);
......@@ -307,7 +307,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
u32 temp;
#ifdef DEBUG
if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
if (ehci->rh_state != EHCI_RH_RUNNING)
BUG ();
#endif
......@@ -356,7 +356,7 @@ static void ehci_iaa_watchdog(unsigned long param)
*/
if (ehci->reclaim
&& !timer_pending(&ehci->iaa_watchdog)
&& HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
&& ehci->rh_state == EHCI_RH_RUNNING) {
u32 cmd, status;
/* If we get here, IAA is *REALLY* late. It's barely
......@@ -496,7 +496,7 @@ static void ehci_work (struct ehci_hcd *ehci)
* misplace IRQs, and should let us run completely without IRQs.
* such lossage has been observed on both VT6202 and VT8235.
*/
if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) &&
if (ehci->rh_state == EHCI_RH_RUNNING &&
(ehci->async->qh_next.ptr != NULL ||
ehci->periodic_sched != 0))
timer_action (ehci, TIMER_IO_WATCHDOG);
......@@ -516,7 +516,7 @@ static void ehci_stop (struct usb_hcd *hcd)
del_timer_sync(&ehci->iaa_watchdog);
spin_lock_irq(&ehci->lock);
if (HC_IS_RUNNING (hcd->state))
if (ehci->rh_state == EHCI_RH_RUNNING)
ehci_quiesce (ehci);
ehci_silence_controller(ehci);
......@@ -741,7 +741,7 @@ static int ehci_run (struct usb_hcd *hcd)
* be started before the port switching actions could complete.
*/
down_write(&ehci_cf_port_reset_rwsem);
hcd->state = HC_STATE_RUNNING;
ehci->rh_state = EHCI_RH_RUNNING;
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
msleep(5);
......@@ -788,7 +788,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* Shared IRQ? */
masked_status = status & INTR_MASK;
if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) {
if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) {
spin_unlock(&ehci->lock);
return IRQ_NONE;
}
......@@ -952,7 +952,7 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
/* failfast */
if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim)
if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim)
end_unlink_async(ehci);
/* If the QH isn't linked then there's nothing we can do
......@@ -1079,7 +1079,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
goto idle_timeout;
}
if (!HC_IS_RUNNING (hcd->state))
if (ehci->rh_state != EHCI_RH_RUNNING)
qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) {
case QH_STATE_LINKED:
......@@ -1291,6 +1291,16 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_grlib_driver
#endif
#ifdef CONFIG_USB_PXA168_EHCI
#include "ehci-pxa168.c"
#define PLATFORM_DRIVER ehci_pxa168_driver
#endif
#ifdef CONFIG_NLM_XLR
#include "ehci-xls.c"
#define PLATFORM_DRIVER ehci_xls_driver
#endif
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
!defined(XILINX_OF_PLATFORM_DRIVER)
......
......@@ -236,10 +236,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
/* stop schedules, clean any completed work */
if (HC_IS_RUNNING(hcd->state)) {
if (ehci->rh_state == EHCI_RH_RUNNING)
ehci_quiesce (ehci);
hcd->state = HC_STATE_QUIESCING;
}
ehci->command = ehci_readl(ehci, &ehci->regs->command);
ehci_work(ehci);
......@@ -313,7 +311,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
/* turn off now-idle HC */
ehci_halt (ehci);
hcd->state = HC_STATE_SUSPENDED;
ehci->rh_state = EHCI_RH_SUSPENDED;
if (ehci->reclaim)
end_unlink_async(ehci);
......@@ -382,6 +380,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* restore CMD_RUN, framelist size, and irq threshold */
ehci_writel(ehci, ehci->command, &ehci->regs->command);
ehci->rh_state = EHCI_RH_RUNNING;
/* Some controller/firmware combinations need a delay during which
* they set up the port statuses. See Bugzilla #8190. */
......@@ -451,7 +450,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
}
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
hcd->state = HC_STATE_RUNNING;
/* Now we can safely re-enable irqs */
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
......@@ -563,7 +561,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
if (ehci->rh_state != EHCI_RH_RUNNING)
return 0;
/* init status to no-changes */
......
......@@ -439,7 +439,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
#endif
......
/*
* drivers/usb/host/ehci-pxa168.c
*
* Tanmay Upadhyay <tanmay.upadhyay@einfochips.com>
*
* Based on drivers/usb/host/ehci-orion.c
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <mach/pxa168.h>
#define USB_PHY_CTRL_REG 0x4
#define USB_PHY_PLL_REG 0x8
#define USB_PHY_TX_REG 0xc
#define FBDIV_SHIFT 4
#define ICP_SHIFT 12
#define ICP_15 2
#define ICP_20 3
#define ICP_25 4
#define KVCO_SHIFT 15
#define PLLCALI12_SHIFT 25
#define CALI12_VDD 0
#define CALI12_09 1
#define CALI12_10 2
#define CALI12_11 3
#define PLLVDD12_SHIFT 27
#define VDD12_VDD 0
#define VDD12_10 1
#define VDD12_11 2
#define VDD12_12 3
#define PLLVDD18_SHIFT 29
#define VDD18_19 0
#define VDD18_20 1
#define VDD18_21 2
#define VDD18_22 3
#define PLL_READY (1 << 23)
#define VCOCAL_START (1 << 21)
#define REG_RCAL_START (1 << 12)
struct pxa168_usb_drv_data {
struct ehci_hcd ehci;
struct clk *pxa168_usb_clk;
struct resource *usb_phy_res;
void __iomem *usb_phy_reg_base;
};
static int ehci_pxa168_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int retval;
ehci_reset(ehci);
retval = ehci_halt(ehci);
if (retval)
return retval;
/*
* data structure init
*/
retval = ehci_init(hcd);
if (retval)
return retval;
hcd->has_tt = 1;
ehci_port_power(ehci, 0);
return retval;
}
static const struct hc_driver ehci_pxa168_hc_driver = {
.description = hcd_name,
.product_desc = "Marvell PXA168 EHCI",
.hcd_priv_size = sizeof(struct pxa168_usb_drv_data),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_pxa168_setup,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.endpoint_reset = ehci_endpoint_reset,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
static int pxa168_usb_phy_init(struct platform_device *pdev)
{
struct resource *res;
void __iomem *usb_phy_reg_base;
struct pxa168_usb_pdata *pdata;
struct pxa168_usb_drv_data *drv_data;
struct usb_hcd *hcd = platform_get_drvdata(pdev);
unsigned long reg_val;
int pll_retry_cont = 10000, err = 0;
drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv;
pdata = (struct pxa168_usb_pdata *)pdev->dev.platform_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev,
"Found HC with no PHY register addr. Check %s setup!\n",
dev_name(&pdev->dev));
return -ENODEV;
}
if (!request_mem_region(res->start, resource_size(res),
ehci_pxa168_hc_driver.description)) {
dev_dbg(&pdev->dev, "controller already in use\n");
return -EBUSY;
}
usb_phy_reg_base = ioremap(res->start, resource_size(res));
if (usb_phy_reg_base == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
err = -EFAULT;
goto err1;
}
drv_data->usb_phy_reg_base = usb_phy_reg_base;
drv_data->usb_phy_res = res;
/* If someone wants to init USB phy in board specific way */
if (pdata && pdata->phy_init)
return pdata->phy_init(usb_phy_reg_base);
/* Power up the PHY and PLL */
writel(readl(usb_phy_reg_base + USB_PHY_CTRL_REG) | 0x3,
usb_phy_reg_base + USB_PHY_CTRL_REG);
/* Configure PHY PLL */
reg_val = readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~(0x7e03ffff);
reg_val |= (VDD18_22 << PLLVDD18_SHIFT | VDD12_12 << PLLVDD12_SHIFT |
CALI12_11 << PLLCALI12_SHIFT | 3 << KVCO_SHIFT |
ICP_15 << ICP_SHIFT | 0xee << FBDIV_SHIFT | 0xb);
writel(reg_val, usb_phy_reg_base + USB_PHY_PLL_REG);
/* Make sure PHY PLL is ready */
while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) {
if (!(pll_retry_cont--)) {
dev_dbg(&pdev->dev, "USB PHY PLL not ready\n");
err = -EIO;
goto err2;
}
}
/* Toggle VCOCAL_START bit of U2PLL for PLL calibration */
udelay(200);
writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) | VCOCAL_START,
usb_phy_reg_base + USB_PHY_PLL_REG);
udelay(40);
writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~VCOCAL_START,
usb_phy_reg_base + USB_PHY_PLL_REG);
/* Toggle REG_RCAL_START bit of U2PTX for impedance calibration */
udelay(400);
writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) | REG_RCAL_START,
usb_phy_reg_base + USB_PHY_TX_REG);
udelay(40);
writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) & ~REG_RCAL_START,
usb_phy_reg_base + USB_PHY_TX_REG);
/* Make sure PHY PLL is ready again */
pll_retry_cont = 0;
while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) {
if (!(pll_retry_cont--)) {
dev_dbg(&pdev->dev, "USB PHY PLL not ready\n");
err = -EIO;
goto err2;
}
}
return 0;
err2:
iounmap(usb_phy_reg_base);
err1:
release_mem_region(res->start, resource_size(res));
return err;
}
static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev)
{
struct resource *res;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct pxa168_usb_drv_data *drv_data;
void __iomem *regs;
int irq, err = 0;
if (usb_disabled())
return -ENODEV;
pr_debug("Initializing pxa168-SoC USB Host Controller\n");
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
if (!request_mem_region(res->start, resource_size(res),
ehci_pxa168_hc_driver.description)) {
dev_dbg(&pdev->dev, "controller already in use\n");
err = -EBUSY;
goto err1;
}
regs = ioremap(res->start, resource_size(res));
if (regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
err = -EFAULT;
goto err2;
}
hcd = usb_create_hcd(&ehci_pxa168_hc_driver,
&pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
err = -ENOMEM;
goto err3;
}
drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv;
/* Enable USB clock */
drv_data->pxa168_usb_clk = clk_get(&pdev->dev, "PXA168-USBCLK");
if (IS_ERR(drv_data->pxa168_usb_clk)) {
dev_err(&pdev->dev, "Couldn't get USB clock\n");
err = PTR_ERR(drv_data->pxa168_usb_clk);
goto err4;
}
clk_enable(drv_data->pxa168_usb_clk);
err = pxa168_usb_phy_init(pdev);
if (err) {
dev_err(&pdev->dev, "USB PHY initialization failed\n");
goto err5;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
hcd->regs = regs;
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs + 0x100;
ehci->regs = hcd->regs + 0x100 +
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
hcd->has_tt = 1;
ehci->sbrn = 0x20;
err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
if (err)
goto err5;
return 0;
err5:
clk_disable(drv_data->pxa168_usb_clk);
clk_put(drv_data->pxa168_usb_clk);
err4:
usb_put_hcd(hcd);
err3:
iounmap(regs);
err2:
release_mem_region(res->start, resource_size(res));
err1:
dev_err(&pdev->dev, "init %s fail, %d\n",
dev_name(&pdev->dev), err);
return err;
}
static int __exit ehci_pxa168_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct pxa168_usb_drv_data *drv_data =
(struct pxa168_usb_drv_data *)hcd->hcd_priv;
usb_remove_hcd(hcd);
/* Power down PHY & PLL */
writel(readl(drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG) & (~0x3),
drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG);
clk_disable(drv_data->pxa168_usb_clk);
clk_put(drv_data->pxa168_usb_clk);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
iounmap(drv_data->usb_phy_reg_base);
release_mem_region(drv_data->usb_phy_res->start,
resource_size(drv_data->usb_phy_res));
usb_put_hcd(hcd);
return 0;
}
MODULE_ALIAS("platform:pxa168-ehci");
static struct platform_driver ehci_pxa168_driver = {
.probe = ehci_pxa168_drv_probe,
.remove = __exit_p(ehci_pxa168_drv_remove),
.shutdown = usb_hcd_platform_shutdown,
.driver.name = "pxa168-ehci",
};
此差异已折叠。
......@@ -270,7 +270,7 @@ static int s5p_ehci_resume(struct device *dev)
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
ehci->rh_state = EHCI_RH_SUSPENDED;
return 0;
}
......
......@@ -479,7 +479,6 @@ static int enable_periodic (struct ehci_hcd *ehci)
cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE;
ehci_writel(ehci, cmd, &ehci->regs->command);
/* posted write ... PSS happens later */
ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
/* make sure ehci_work scans these */
ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index)
......@@ -677,7 +676,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* reschedule QH iff another request is queued */
if (!list_empty(&qh->qtd_list) &&
HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
ehci->rh_state == EHCI_RH_RUNNING) {
rc = qh_schedule(ehci, qh);
/* An error here likely indicates handshake failure
......@@ -2275,7 +2274,7 @@ scan_periodic (struct ehci_hcd *ehci)
* Touches as few pages as possible: cache-friendly.
*/
now_uframe = ehci->next_uframe;
if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
if (ehci->rh_state == EHCI_RH_RUNNING) {
clock = ehci_readl(ehci, &ehci->regs->frame_index);
clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
} else {
......@@ -2310,7 +2309,7 @@ scan_periodic (struct ehci_hcd *ehci)
union ehci_shadow temp;
int live;
live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
live = (ehci->rh_state == EHCI_RH_RUNNING);
switch (hc32_to_cpu(ehci, type)) {
case Q_TYPE_QH:
/* handle any completions */
......@@ -2435,7 +2434,7 @@ scan_periodic (struct ehci_hcd *ehci)
* We can't advance our scan without collecting the ISO
* transfers that are still pending in this frame.
*/
if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
ehci->next_uframe = now_uframe;
break;
}
......@@ -2451,7 +2450,7 @@ scan_periodic (struct ehci_hcd *ehci)
if (now_uframe == clock) {
unsigned now;
if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)
if (ehci->rh_state != EHCI_RH_RUNNING
|| ehci->periodic_sched == 0)
break;
ehci->next_uframe = now_uframe;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_hcd_ath79_driver
#endif
#ifdef CONFIG_NLM_XLR
#include "ohci-xls.c"
#define PLATFORM_DRIVER ohci_xls_driver
#endif
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OMAP1_PLATFORM_DRIVER) && \
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册