[PATCH v10 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function

Keshava Munegowda keshava_mgowda at ti.com
Tue Mar 29 21:55:47 EST 2011


> -----Original Message-----
> From: linux-usb-owner at vger.kernel.org
[mailto:linux-usb-owner at vger.kernel.org] On Behalf Of
> tmarri at apm.com
> Sent: Monday, March 28, 2011 11:58 PM
> To: linux-usb at vger.kernel.org; linuxppc-dev at lists.ozlabs.org
> Cc: greg at kroah.com; tmarri at apm.com; Fushen Chen; Mark Miesfeld
> Subject: [PATCH v10 04/10] USB/ppc4xx: Add Synopsys DWC OTG HCD function
>
> From: Tirumala Marri <tmarri at apm.com>
>
> Implements DWC OTG USB Host Controller Driver (HCD) and interface to
> USB Host controller Driver framework.
>
> Signed-off-by: Tirumala R Marri <tmarri at apm.com>
> Signed-off-by: Fushen Chen <fchen at apm.com>
> Signed-off-by: Mark Miesfeld <mmiesfeld at apm.com>
> ---
>  drivers/usb/otg/dwc/hcd.c | 2465
+++++++++++++++++++++++++++++++++++++++++++++
>  drivers/usb/otg/dwc/hcd.h |  416 ++++++++
>  2 files changed, 2881 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/usb/otg/dwc/hcd.c
>  create mode 100644 drivers/usb/otg/dwc/hcd.h
>
> diff --git a/drivers/usb/otg/dwc/hcd.c b/drivers/usb/otg/dwc/hcd.c
> new file mode 100644
> index 0000000..884bf31
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/hcd.c
> @@ -0,0 +1,2465 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld at apm.com>
> + * Modified by Stefan Roese <sr at denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck at theptrgroup.com>
> + *
> + * 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 SYNOPSYS, INC. 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.
> + *
> + */
> +
> +/*
> + * This file contains the implementation of the HCD. In Linux, the HCD
> + * implements the hc_driver API.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "hcd.h"
> +
> +static const char dwc_otg_hcd_name[] = "dwc_otg_hcd";
> +
> +/**
> + * Clears the transfer state for a host channel. This function is
normally
> + * called after a transfer is done and the host channel is being
released. It
> + * clears the channel interrupt enables and any unhandled channel
interrupt
> + * conditions.
> + */
> +void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc *hc)
> +{
> +	ulong regs;
> +
> +	hc->xfer_started = 0;
> +	regs = core_if->host_if->hc_regs[hc->hc_num];
> +	dwc_write32(regs + DWC_HCINTMSK, 0);
> +	dwc_write32(regs + DWC_HCINT, 0xFFFFFFFF);
> +}
> +
> +/**
> + * This function enables the Host mode interrupts.
> + */
> +static void dwc_otg_enable_host_interrupts(struct core_if *core_if)
> +{
> +	ulong global_regs = core_if->core_global_regs;
> +	u32 intr_mask = 0;
> +
> +	/* Disable all interrupts. */
> +	dwc_write32(global_regs + DWC_GINTMSK, 0);
> +
> +	/* Clear any pending interrupts. */
> +	dwc_write32(global_regs + DWC_GINTSTS, 0xFFFFFFFF);
> +
> +	/* Enable the common interrupts */
> +	dwc_otg_enable_common_interrupts(core_if);
> +
> +	/*
> +	 * Enable host mode interrupts without disturbing common
> +	 * interrupts.
> +	 */
> +	intr_mask |= DWC_INTMSK_STRT_OF_FRM;
> +	intr_mask |= DWC_INTMSK_HST_PORT;
> +	intr_mask |= DWC_INTMSK_HST_CHAN;
> +	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, intr_mask);
> +}
> +
> +/**
> + * This function initializes the DWC_otg controller registers for
> + * host mode.
> + *
> + * This function flushes the Tx and Rx FIFOs and it flushes any entries
in the
> + * request queues. Host channels are reset to ensure that they are
ready for
> + * performing transfers.
> + */
> +static void dwc_otg_core_host_init(struct core_if *core_if)
> +{
> +	ulong global_regs = core_if->core_global_regs;
> +	struct dwc_host_if *host_if = core_if->host_if;
> +	struct core_params *params = core_if->core_params;
> +	u32 hprt0 = 0;
> +	u32 nptxfifosize = 0;
> +	u32 ptxfifosize = 0;
> +	u32 i;
> +	u32 hcchar;
> +	ulong hcfg;
> +	ulong hc_regs;
> +	int num_channels;
> +	u32 gotgctl = 0;

Tabify the declarations ;


> +
> +	/* Restart the Phy Clock */
> +	dwc_write32(core_if->pcgcctl, 0);
> +
> +	/* Initialize Host Configuration Register */
> +	init_fslspclksel(core_if);
> +	if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) {
> +		hcfg = dwc_read32(host_if->host_global_regs + DWC_HCFG);
> +		hcfg = DWC_HCFG_FSLSUPP_RW(hcfg, 1);
> +		dwc_write32(host_if->host_global_regs + DWC_HCFG, hcfg);
> +	}
> +
> +	/* Configure data FIFO sizes */
> +	if (DWC_HWCFG2_DYN_FIFO_RD(core_if->hwcfg2)
> +	    && params->enable_dynamic_fifo) {
> +		/* Rx FIFO */
> +		dwc_write32(global_regs + DWC_GRXFSIZ,
> +			    params->host_rx_fifo_size);
> +
> +		/* Non-periodic Tx FIFO */
> +		nptxfifosize = DWC_RX_FIFO_DEPTH_WR(nptxfifosize,
> +						    params->
> +
host_nperio_tx_fifo_size);
> +		nptxfifosize =
> +		    DWC_RX_FIFO_START_ADDR_WR(nptxfifosize,
> +					      params->host_rx_fifo_size);
> +		dwc_write32(global_regs + DWC_GNPTXFSIZ, nptxfifosize);
> +
> +		/* Periodic Tx FIFO */
> +		ptxfifosize = DWC_RX_FIFO_DEPTH_WR(ptxfifosize,
> +						   params->
> +
host_perio_tx_fifo_size);
> +		ptxfifosize =
> +		    DWC_RX_FIFO_START_ADDR_WR(ptxfifosize,
> +					      (DWC_RX_FIFO_START_ADDR_RD
> +					       (nptxfifosize) +
> +					       DWC_RX_FIFO_DEPTH_RD
> +					       (nptxfifosize)));
> +		dwc_write32(global_regs + DWC_HPTXFSIZ, ptxfifosize);
> +	}
> +
> +	/* Clear Host Set HNP Enable in the OTG Control Register */
> +	gotgctl |= DWC_GCTL_HOST_HNP_ENA;
> +	dwc_modify32(global_regs + DWC_GOTGCTL, gotgctl, 0);
> +
> +	/* Make sure the FIFOs are flushed. */
> +	dwc_otg_flush_tx_fifo(core_if, DWC_GRSTCTL_TXFNUM_ALL);
> +	dwc_otg_flush_rx_fifo(core_if);
> +
> +	/* Flush out any leftover queued requests. */
> +	num_channels = core_if->core_params->host_channels;
> +	for (i = 0; i < num_channels; i++) {
> +		hc_regs = core_if->host_if->hc_regs[i];
> +		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> +		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> +		hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
> +		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +	}
> +
> +	/* Halt all channels to put them into a known state. */
> +	for (i = 0; i < num_channels; i++) {
> +		int count = 0;
> +
> +		hc_regs = core_if->host_if->hc_regs[i];
> +		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> +		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> +		hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, 0);
> +		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> +		do {
> +			hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +			if (++count > 200) {
> +				pr_err("%s: Unable to clear halt on "
> +				       "channel %d\n", __func__, i);
> +				break;
> +			}
> +			udelay(100);
> +		} while (DWC_HCCHAR_ENA_RD(hcchar));
> +	}
> +
> +	/* Turn on the vbus power. */
> +	pr_info("Init: Port Power? op_state=%s\n",
> +		op_state_str(core_if->xceiv->state));
> +
> +	if (core_if->xceiv->state == OTG_STATE_A_HOST) {
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		pr_info("Init: Power Port (%d)\n",
DWC_HPRT0_PRT_PWR_RD(hprt0));
> +		if (DWC_HPRT0_PRT_PWR_RD(hprt0) == 0) {
> +			hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
> +			dwc_write32(host_if->hprt0, hprt0);
> +		}
> +	}
> +	dwc_otg_enable_host_interrupts(core_if);
> +}
> +
> +/**
> + * Initializes dynamic portions of the DWC_otg HCD state.
> + */
> +static void hcd_reinit(struct dwc_hcd *hcd)
> +{
> +	struct list_head *item;
> +	int num_channels;
> +	u32 i;
> +	struct dwc_hc *channel;

Tabify the declarations ;

> +
> +	hcd->flags.d32 = 0;
> +	hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active;
> +	hcd->available_host_channels =
hcd->core_if->core_params->host_channels;
> +
> +	/*
> +	 * Put all channels in the free channel list and clean up channel
> +	 * states.
> +	 */
> +	item = hcd->free_hc_list.next;
> +	while (item != &hcd->free_hc_list) {
> +		list_del(item);
> +		item = hcd->free_hc_list.next;
> +	}
> +
> +	num_channels = hcd->core_if->core_params->host_channels;
> +	for (i = 0; i < num_channels; i++) {
> +		channel = hcd->hc_ptr_array[i];
> +		list_add_tail(&channel->hc_list_entry,
&hcd->free_hc_list);
> +		dwc_otg_hc_cleanup(hcd->core_if, channel);
> +	}
> +
> +	/* Initialize the DWC core for host mode operation. */
> +	dwc_otg_core_host_init(hcd->core_if);
> +}
> +
> +/* Gets the dwc_hcd from a struct usb_hcd */
> +static inline struct dwc_hcd *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd)
> +{
> +	return (struct dwc_hcd *)hcd->hcd_priv;
> +}
> +
> +/**
> + * Initializes the DWC_otg controller and its root hub and prepares it
for host
> + * mode operation. Activates the root port. Returns 0 on success and a
negative
> + * error code on failure.
> +*/
> +static int dwc_otg_hcd_start(struct usb_hcd *hcd)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	struct usb_bus *bus = hcd_to_bus(hcd);
> +
> +	hcd->state = HC_STATE_RUNNING;
> +
> +	/* Inform the HUB driver to resume. */
> +	if (bus->root_hub)
> +		usb_hcd_resume_root_hub(hcd);
> +
> +	hcd_reinit(dwc_hcd);
> +	return 0;
> +}
> +
> +/**
> + * Work queue function for starting the HCD when A-Cable is connected.
> + * The dwc_otg_hcd_start() must be called in a process context.
> + */
> +static void hcd_start_func(struct work_struct *work)
> +{
> +	struct dwc_hcd *priv = container_of(work, struct dwc_hcd,
start_work);
> +	struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p;
> +
> +	if (usb_hcd)
> +		dwc_otg_hcd_start(usb_hcd);
> +}
> +
> +/**
> + * HCD Callback function for starting the HCD when A-Cable is
> + * connected.
> + */
> +static int dwc_otg_hcd_start_cb(void *_p)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(_p);
> +	struct core_if *core_if = dwc_hcd->core_if;
> +	u32 hprt0;

Tabify the declarations ;

> +
> +	if (core_if->xceiv->state == OTG_STATE_B_HOST) {
> +		/*
> +		 * Reset the port.  During a HNP mode switch the reset
> +		 * needs to occur within 1ms and have a duration of at
> +		 * least 50ms.
> +		 */
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		((struct usb_hcd *)_p)->self.is_b_host = 1;
> +	} else {
> +		((struct usb_hcd *)_p)->self.is_b_host = 0;
> +	}
> +
> +	/* Need to start the HCD in a non-interrupt context. */
> +	dwc_hcd->_p = _p;
> +	schedule_work(&dwc_hcd->start_work);
> +	return 1;
> +}
> +
> +/**
> + * This function disables the Host Mode interrupts.
> + */
> +static void dwc_otg_disable_host_interrupts(struct core_if *core_if)
> +{
> +	u32 global_regs = core_if->core_global_regs;
> +	u32 intr_mask = 0;
> +
> +	/*
> +	 * Disable host mode interrupts without disturbing common
> +	 * interrupts.
> +	 */
> +	intr_mask |= DWC_INTMSK_STRT_OF_FRM;
> +	intr_mask |= DWC_INTMSK_HST_PORT;
> +	intr_mask |= DWC_INTMSK_HST_CHAN;
> +	intr_mask |= DWC_INTMSK_P_TXFIFO_EMPTY;
> +	intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +	dwc_modify32(global_regs + DWC_GINTMSK, intr_mask, 0);
> +}
> +
> +/**
> + * Halts the DWC_otg host mode operations in a clean manner. USB
transfers are
> + * stopped.
> + */
> +static void dwc_otg_hcd_stop(struct usb_hcd *hcd)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	u32 hprt0 = 0;
> +
> +	/* Turn off all host-specific interrupts. */
> +	dwc_otg_disable_host_interrupts(dwc_hcd->core_if);
> +
> +	/*
> +	 * The root hub should be disconnected before this function is
called.
> +	 * The disconnect will clear the QTD lists (via
..._hcd_urb_dequeue)
> +	 * and the QH lists (via ..._hcd_endpoint_disable).
> +	 */
> +
> +	/* Turn off the vbus power */
> +	pr_info("PortPower off\n");
> +	hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> +	dwc_write32(dwc_hcd->core_if->host_if->hprt0, hprt0);
> +}
> +
> +/**
> + * HCD Callback function for stopping the HCD.
> + */
> +static int dwc_otg_hcd_stop_cb(void *_p)
> +{
> +	struct usb_hcd *usb_hcd = (struct usb_hcd *)_p;
> +
> +	dwc_otg_hcd_stop(usb_hcd);
> +	return 1;
> +}
> +
> +static void del_timers(struct dwc_hcd *hcd)
> +{
> +	del_timer_sync(&hcd->conn_timer);
> +}
> +
> +/**
> + * Processes all the URBs in a single list of QHs. Completes them with
> + * -ETIMEDOUT and frees the QTD.
> + */
> +static void kill_urbs_in_qh_list(struct dwc_hcd *hcd, struct list_head
*qh_list)
> +{
> +	struct list_head *qh_item, *q;
> +
> +	qh_item = qh_list->next;
> +	list_for_each_safe(qh_item, q, qh_list) {
> +		struct dwc_qh *qh;
> +		struct list_head *qtd_item;
> +		struct dwc_qtd *qtd;
> +
> +		qh = list_entry(qh_item, struct dwc_qh, qh_list_entry);
> +		qtd_item = qh->qtd_list.next;
> +		qtd = list_entry(qtd_item, struct dwc_qtd,
qtd_list_entry);
> +		if (qtd->urb != NULL) {
> +			spin_lock(&hcd->lock);
> +			dwc_otg_hcd_complete_urb(hcd, qtd->urb,
-ETIMEDOUT);
> +			dwc_otg_hcd_qtd_remove_and_free(qtd);
> +			spin_unlock(&hcd->lock);
> +		}
> +	}
> +}
> +
> +/**
> + * Responds with an error status of ETIMEDOUT to all URBs in the
non-periodic
> + * and periodic schedules. The QTD associated with each URB is removed
from
> + * the schedule and freed. This function may be called when a
disconnect is
> + * detected or when the HCD is being stopped.
> + */
> +static void kill_all_urbs(struct dwc_hcd *hcd)
> +{
> +	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_deferred);
> +	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive);
> +	kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active);
> +	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive);
> +	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready);
> +	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned);
> +	kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued);
> +}
> +
> +/**
> + * HCD Callback function for disconnect of the HCD.
> + */
> +static int dwc_otg_hcd_disconnect_cb(void *_p)
> +{
> +	u32 intr;
> +	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
> +	struct core_if *core_if = hcd->core_if;
> +
> +	/* Set status flags for the hub driver. */
> +	hcd->flags.b.port_connect_status_change = 1;
> +	hcd->flags.b.port_connect_status = 0;
> +
> +	/*
> +	 * Shutdown any transfers in process by clearing the Tx FIFO Empty
> +	 * interrupt mask and status bits and disabling subsequent host
> +	 * channel interrupts.
> +	 */
> +	intr = 0;
> +	intr |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +	intr |= DWC_INTMSK_P_TXFIFO_EMPTY;
> +	intr |= DWC_INTMSK_HST_CHAN;
> +	dwc_modify32(gintmsk_reg(hcd), intr, 0);
> +	dwc_modify32(gintsts_reg(hcd), intr, 0);
> +
> +	del_timers(hcd);
> +
> +	/*
> +	 * Turn off the vbus power only if the core has transitioned to
device
> +	 * mode. If still in host mode, need to keep power on to detect a
> +	 * reconnection.
> +	 */
> +	if (dwc_otg_is_device_mode(core_if)) {
> +		if (core_if->xceiv->state != OTG_STATE_A_SUSPEND) {
> +			u32 hprt0 = 0;
> +
> +			pr_info("Disconnect: PortPower off\n");
> +			hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> +			dwc_write32(core_if->host_if->hprt0, hprt0);
> +		}
> +		dwc_otg_disable_host_interrupts(core_if);
> +	}
> +
> +	/* Respond with an error status to all URBs in the schedule. */
> +	kill_all_urbs(hcd);
> +	if (dwc_otg_is_host_mode(core_if)) {
> +		/* Clean up any host channels that were in use. */
> +		int num_channels;
> +		u32 i;
> +		struct dwc_hc *channel;
> +		ulong regs;
> +		u32 hcchar;
> +
> +		num_channels = core_if->core_params->host_channels;
> +		if (!core_if->dma_enable) {
> +			/* Flush out any channel requests in slave mode.
*/
> +			for (i = 0; i < num_channels; i++) {
> +				channel = hcd->hc_ptr_array[i];
> +				if (list_empty(&channel->hc_list_entry)) {
> +					regs =
> +					    core_if->host_if->hc_regs[i];
> +					hcchar = dwc_read32(regs +
DWC_HCCHAR);
> +
> +					if (DWC_HCCHAR_ENA_RD(hcchar)) {
> +						hcchar =
> +
DWC_HCCHAR_ENA_RW(hcchar,
> +								      0);
> +						hcchar =
> +
DWC_HCCHAR_DIS_RW(hcchar,
> +								      1);
> +						hcchar =
> +
DWC_HCCHAR_EPDIR_RW(hcchar,
> +
0);
> +						dwc_write32(regs +
DWC_HCCHAR,
> +							    hcchar);
> +					}
> +				}
> +			}
> +		}
> +
> +		for (i = 0; i < num_channels; i++) {
> +			channel = hcd->hc_ptr_array[i];
> +			if (list_empty(&channel->hc_list_entry)) {
> +				regs = core_if->host_if->hc_regs[i];
> +				hcchar = dwc_read32(regs + DWC_HCCHAR);
> +
> +				if (DWC_HCCHAR_ENA_RD(hcchar)) {
> +					/* Halt the channel. */
> +					hcchar = DWC_HCCHAR_DIS_RW(hcchar,
1);
> +					dwc_write32(regs + DWC_HCCHAR,
hcchar);
> +				}
> +				dwc_otg_hc_cleanup(core_if, channel);
> +				list_add_tail(&channel->hc_list_entry,
> +					      &hcd->free_hc_list);
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * A disconnect will end the session so the B-Device is no
> +	 * longer a B-host.
> +	 */
> +	((struct usb_hcd *)_p)->self.is_b_host = 0;
> +	return 1;
> +}
> +
> +/**
> + * Connection timeout function.  An OTG host is required to display a
> + * message if the device does not connect within 10 seconds.
> + */
> +static void dwc_otg_hcd_connect_timeout(unsigned long _ptr)
> +{
> +	pr_info("Connect Timeout\n");
> +	pr_err("Device Not Connected/Responding\n");
> +}
> +
> +/**
> + * Start the connection timer.  An OTG host is required to display a
> + * message if the device does not connect within 10 seconds.  The
> + * timer is deleted if a port connect interrupt occurs before the
> + * timer expires.
> + */
> +static void dwc_otg_hcd_start_connect_timer(struct dwc_hcd *hcd)
> +{
> +	init_timer(&hcd->conn_timer);
> +	hcd->conn_timer.function = dwc_otg_hcd_connect_timeout;
> +	hcd->conn_timer.data = (unsigned long)0;
> +	hcd->conn_timer.expires = jiffies + (HZ * 10);
> +	add_timer(&hcd->conn_timer);
> +}
> +
> +/**
> + * HCD Callback function for disconnect of the HCD.
> + */
> +static int dwc_otg_hcd_session_start_cb(void *_p)
> +{
> +	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_p);
> +
> +	dwc_otg_hcd_start_connect_timer(hcd);
> +	return 1;
> +}
> +
> +/* HCD Callback structure for handling mode switching. */
> +static struct cil_callbacks hcd_cil_callbacks = {
> +	.start = dwc_otg_hcd_start_cb,
> +	.stop = dwc_otg_hcd_stop_cb,
> +	.disconnect = dwc_otg_hcd_disconnect_cb,
> +	.session_start = dwc_otg_hcd_session_start_cb,
> +	.p = NULL,
> +};
> +
> +/*
> + * Reset Workqueue implementation
> + */
> +static void port_reset_wqfunc(struct work_struct *work)
> +{
> +	struct dwc_hcd *hcd = container_of(work, struct dwc_hcd,
> +					   usb_port_reset);
> +	struct core_if *core_if = hcd->core_if;
> +	u32 hprt0 = 0;
> +	unsigned long flags;
> +
> +	pr_info("%s\n", __func__);
> +	spin_lock_irqsave(&hcd->lock, flags);
> +	hprt0 = dwc_otg_read_hprt0(core_if);
> +	hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> +	dwc_write32(core_if->host_if->hprt0, hprt0);
> +	spin_unlock_irqrestore(&hcd->lock, flags);
> +	msleep(60);
> +	spin_lock_irqsave(&hcd->lock, flags);
> +	hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
> +	dwc_write32(core_if->host_if->hprt0, hprt0);
> +	hcd->flags.b.port_reset_change = 1;
> +	spin_unlock_irqrestore(&hcd->lock, flags);
> +}
> +
> +/*
> + * Wakeup Workqueue implementation
> + */
> +static void port_wakeup_wqfunc(struct work_struct *work)
> +{
> +	struct core_if *core_if = container_of(to_delayed_work(work),
> +					       struct core_if,
usb_port_wakeup);
> +	u32 hprt0;
> +
> +	pr_info("%s\n", __func__);
> +	/* Now wait for 70 ms. */
> +	hprt0 = dwc_otg_read_hprt0(core_if);
> +	msleep(70);
> +	hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
> +	dwc_write32(core_if->host_if->hprt0, hprt0);
> +}
> +
> +/**
> + * Starts processing a USB transfer request specified by a USB Request
Block
> + * (URB). mem_flags indicates the type of memory allocation to use
while
> + * processing this URB.
> + */
> +static int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb
*urb,
> +				   gfp_t _mem_flags)
> +{
> +	int retval;
> +	unsigned long flags;
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	struct dwc_qtd *qtd;
> +
> +	if (!dwc_hcd->flags.b.port_connect_status) {
> +		/* No longer connected. */
> +		retval = -ENODEV;
> +		goto err_enq;
> +	}
> +
> +	qtd = dwc_otg_hcd_qtd_create(urb, _mem_flags);
> +	if (!qtd) {
> +		pr_err("DWC OTG HCD URB Enqueue failed creating "
"QTD\n");
> +		retval = -ENOMEM;
> +		goto err_enq;
> +	}
> +
> +	spin_lock_irqsave(&dwc_hcd->lock, flags);
> +	retval = usb_hcd_link_urb_to_ep(hcd, urb);
> +	if (unlikely(retval))
> +		goto fail;
> +
> +	retval = dwc_otg_hcd_qtd_add(qtd, dwc_hcd);
> +	if (retval < 0) {
> +		pr_err("DWC OTG HCD URB Enqueue failed adding QTD. "
> +		       "Error status %d\n", retval);
> +		usb_hcd_unlink_urb_from_ep(hcd, urb);
> +		goto fail;
> +	}
> +
> +fail:
> +	if (retval)
> +		dwc_otg_hcd_qtd_free(qtd);
> +
> +	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +err_enq:
> +
> +	return retval;
> +}
> +
> +/**
> + * Attempts to halt a host channel. This function should only be called
in
> + * Slave mode or to abort a transfer in either Slave mode or DMA mode.
Under
> + * normal circumstances in DMA mode, the controller halts the channel
when the
> + * transfer is complete or a condition occurs that requires application
> + * intervention.
> + *
> + * In slave mode, checks for a free request queue entry, then sets the
Channel
> + * Enable and Channel Disable bits of the Host Channel Characteristics
> + * register of the specified channel to intiate the halt. If there is
no free
> + * request queue entry, sets only the Channel Disable bit of the
HCCHARn
> + * register to flush requests for this channel. In the latter case,
sets a
> + * flag to indicate that the host channel needs to be halted when a
request
> + * queue slot is open.
> + *
> + * In DMA mode, always sets the Channel Enable and Channel Disable bits
of the
> + * HCCHARn register. The controller ensures there is space in the
request
> + * queue before submitting the halt request.
> + *
> + * Some time may elapse before the core flushes any posted requests for
this
> + * host channel and halts. The Channel Halted interrupt handler
completes the
> + * deactivation of the host channel.
> + */
> +void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
> +		     enum dwc_halt_status hlt_sts)
> +{
> +	u32 nptxsts;
> +	u32 hptxsts = 0;
> +	u32 hcchar;
> +	ulong hc_regs;
> +	ulong global_regs = core_if->core_global_regs;
> +	ulong host_global_regs;
> +
> +	hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +	host_global_regs = core_if->host_if->host_global_regs;
> +
> +	WARN_ON(hlt_sts == DWC_OTG_HC_XFER_NO_HALT_STATUS);
> +
> +	if (hlt_sts == DWC_OTG_HC_XFER_URB_DEQUEUE ||
> +	    hlt_sts == DWC_OTG_HC_XFER_AHB_ERR) {
> +		/*
> +		 * Disable all channel interrupts except Ch Halted. The
QTD
> +		 * and QH state associated with this transfer has been
cleared
> +		 * (in the case of URB_DEQUEUE), so the channel needs to
be
> +		 * shut down carefully to prevent crashes.
> +		 */
> +		u32 hcintmsk;
> +		hcintmsk = 0;
> +		hcintmsk = DWC_HCINTMSK_CHAN_HALTED_RW(hcintmsk, 1);
> +		dwc_write32(hc_regs + DWC_HCINTMSK, hcintmsk);
> +
> +		/*
> +		 * Make sure no other interrupts besides halt are
currently
> +		 * pending. Handling another interrupt could cause a crash
due
> +		 * to the QTD and QH state.
> +		 */
> +		dwc_write32(hc_regs + DWC_HCINT, ~hcintmsk);
> +
> +		/*
> +		 * Make sure the halt status is set to URB_DEQUEUE or
AHB_ERR
> +		 * even if the channel was already halted for some other
reason.
> +		 */
> +		hc->halt_status = hlt_sts;
> +
> +		/*
> +		 * If the channel is not enabled, the channel is either
already
> +		 * halted or it hasn't started yet. In DMA mode, the
transfer
> +		 * may halt if it finishes normally or a condition occurs
that
> +		 * requires driver intervention. Don't want to halt the
channel
> +		 * again. In either Slave or DMA mode, it's possible that
the
> +		 * transfer has been assigned to a channel, but not
started yet
> +		 * when an URB is dequeued. Don't want to halt a channel
that
> +		 * hasn't started yet.
> +		 */
> +		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +		if (!DWC_HCCHAR_ENA_RD(hcchar))
> +			return;
> +	}
> +
> +	if (hc->halt_pending)
> +		/*
> +		 * A halt has already been issued for this channel. This
might
> +		 * happen when a transfer is aborted by a higher level in
> +		 * the stack.
> +		 */
> +		return;
> +
> +	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> +	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 1);
> +	if (!core_if->dma_enable) {
> +		/* Check for space in the request queue to issue the halt.
*/
> +		if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL ||
> +		    hc->ep_type == DWC_OTG_EP_TYPE_BULK) {
> +			nptxsts = dwc_read32(global_regs + DWC_GNPTXSTS);
> +
> +			if (!DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(nptxsts))
> +				hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> +		} else {
> +			hptxsts =
> +			    dwc_read32(host_global_regs + DWC_HPTXSTS);
> +
> +			if (!DWC_HPTXSTS_PTXSPC_AVAIL_RD(hptxsts) ||
> +			    core_if->queuing_high_bandwidth)
> +				hcchar = DWC_HCCHAR_ENA_RW(hcchar, 0);
> +		}
> +	}
> +	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> +	hc->halt_status = hlt_sts;
> +	if (DWC_HCCHAR_ENA_RD(hcchar)) {
> +		hc->halt_pending = 1;
> +		hc->halt_on_queue = 0;
> +	} else {
> +		hc->halt_on_queue = 1;
> +	}
> +}
> +
> +/**
> + * Aborts/cancels a USB transfer request. Always returns 0 to indicate
> + * success.
> + */
> +static int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb
*urb,
> +				   int status)
> +{
> +	unsigned long flags;
> +	struct dwc_hcd *dwc_hcd;
> +	struct dwc_qtd *urb_qtd;
> +	struct dwc_qh *qh;
> +	int retval;
> +
> +	urb_qtd = (struct dwc_qtd *)urb->hcpriv;
> +	if (!urb_qtd)
> +		return -EINVAL;
> +	qh = (struct dwc_qh *)urb_qtd->qtd_qh_ptr;
> +	if (!qh)
> +		return -EINVAL;
> +
> +	dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	spin_lock_irqsave(&dwc_hcd->lock, flags);
> +
> +	retval = usb_hcd_check_unlink_urb(hcd, urb, status);
> +	if (retval) {
> +		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +		return retval;
> +	}
> +
> +	if (urb_qtd == qh->qtd_in_process) {
> +		/* The QTD is in process (it has been assigned to a
channel). */
> +		if (dwc_hcd->flags.b.port_connect_status) {
> +			/*
> +			 * If still connected (i.e. in host mode), halt
the
> +			 * channel so it can be used for other transfers.
If
> +			 * no longer connected, the host registers can't
be
> +			 * written to halt the channel since the core is
in
> +			 * device mode.
> +			 */
> +			dwc_otg_hc_halt(dwc_hcd->core_if, qh->channel,
> +					DWC_OTG_HC_XFER_URB_DEQUEUE);
> +		}
> +	}
> +
> +	/*
> +	 * Free the QTD and clean up the associated QH. Leave the QH in
the
> +	 * schedule if it has any remaining QTDs.
> +	 */
> +	dwc_otg_hcd_qtd_remove_and_free(urb_qtd);
> +	if (qh && urb_qtd == qh->qtd_in_process) {
> +		dwc_otg_hcd_qh_deactivate(dwc_hcd, qh, 0);
> +		qh->channel = NULL;
> +		qh->qtd_in_process = NULL;
> +	} else if (qh && list_empty(&qh->qtd_list)) {
> +		dwc_otg_hcd_qh_remove(dwc_hcd, qh);
> +	}
> +
> +	urb->hcpriv = NULL;
> +	usb_hcd_unlink_urb_from_ep(hcd, urb);
> +	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +
> +	/* Higher layer software sets URB status. */
> +	usb_hcd_giveback_urb(hcd, urb, status);
> +
> +	return 0;
> +}
> +
> +/* Remove and free a QH */
> +static inline void dwc_otg_hcd_qh_remove_and_free(struct dwc_hcd *hcd,
> +						  struct dwc_qh *qh)
> +{
> +	dwc_otg_hcd_qh_remove(hcd, qh);
> +	dwc_otg_hcd_qh_free(qh);
> +}
> +
> +static void qh_list_free(struct dwc_hcd *hcd, struct list_head
*_qh_list)
> +{
> +	struct list_head *item, *tmp;
> +	struct dwc_qh *qh;
> +
> +	/* If the list hasn't been initialized yet, return. */
> +	if (_qh_list->next == NULL)
> +		return;
> +
> +	/* Ensure there are no QTDs or URBs left. */
> +	kill_urbs_in_qh_list(hcd, _qh_list);
> +
> +	list_for_each_safe(item, tmp, _qh_list) {
> +		qh = list_entry(item, struct dwc_qh, qh_list_entry);
> +		dwc_otg_hcd_qh_remove_and_free(hcd, qh);
> +	}
> +}
> +
> +/**
> + * Frees resources in the DWC_otg controller related to a given
endpoint. Also
> + * clears state in the HCD related to the endpoint. Any URBs for the
endpoint
> + * must already be dequeued.
> + */
> +static void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd,
> +					 struct usb_host_endpoint *ep)
> +{
> +	struct dwc_qh *qh;
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	unsigned long flags;

Tabify the declarations ;

> +
> +	spin_lock_irqsave(&dwc_hcd->lock, flags);
> +	qh = (struct dwc_qh *)ep->hcpriv;
> +	if (qh) {
> +		dwc_otg_hcd_qh_remove_and_free(dwc_hcd, qh);
> +		ep->hcpriv = NULL;
> +	}
> +	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +}
> +
> +/**
> + * Creates Status Change bitmap for the root hub and root port. The
bitmap is
> + * returned in buf. Bit 0 is the status change indicator for the root
hub. Bit 1
> + * is the status change indicator for the single root port. Returns 1
if either
> + * change indicator is 1, otherwise returns 0.
> + */
> +static int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *buf)
> +{
> +	struct dwc_hcd *hcd = hcd_to_dwc_otg_hcd(_hcd);
> +
> +	buf[0] = 0;
> +	buf[0] |= (hcd->flags.b.port_connect_status_change
> +		   || hcd->flags.b.port_reset_change
> +		   || hcd->flags.b.port_enable_change
> +		   || hcd->flags.b.port_suspend_change
> +		   || hcd->flags.b.port_over_current_change) << 1;
> +
> +	return (buf[0] != 0);
> +}
> +
> +/* Handles the hub class-specific ClearPortFeature request.*/
> +static int do_clear_port_feature(struct dwc_hcd *hcd, u16 val)
> +{
> +	struct core_if *core_if = hcd->core_if;
> +	u32 hprt0 = 0;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hcd->lock, flags);
> +	switch (val) {
> +	case USB_PORT_FEAT_ENABLE:
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_ENA_RW(hprt0, 1);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		break;
> +	case USB_PORT_FEAT_SUSPEND:
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 1);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +
> +		/* Clear Resume bit */
> +		spin_unlock_irqrestore(&hcd->lock, flags);
> +		msleep(100);
> +		spin_lock_irqsave(&hcd->lock, flags);
> +		hprt0 = DWC_HPRT0_PRT_RES_RW(hprt0, 0);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		break;
> +	case USB_PORT_FEAT_POWER:
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 0);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		break;
> +	case USB_PORT_FEAT_INDICATOR:
> +		/* Port inidicator not supported */
> +		break;
> +	case USB_PORT_FEAT_C_CONNECTION:
> +		/* Clears drivers internal connect status change flag */
> +		hcd->flags.b.port_connect_status_change = 0;
> +		break;
> +	case USB_PORT_FEAT_C_RESET:
> +		/* Clears driver's internal Port Reset Change flag */
> +		hcd->flags.b.port_reset_change = 0;
> +		break;
> +	case USB_PORT_FEAT_C_ENABLE:
> +		/* Clears driver's internal Port Enable/Disable Change
flag  */
> +		hcd->flags.b.port_enable_change = 0;
> +		break;
> +	case USB_PORT_FEAT_C_SUSPEND:
> +		/*
> +		 * Clears the driver's internal Port Suspend
> +		 * Change flag, which is set when resume signaling on
> +		 * the host port is complete
> +		 */
> +		hcd->flags.b.port_suspend_change = 0;
> +		break;
> +	case USB_PORT_FEAT_C_OVER_CURRENT:
> +		hcd->flags.b.port_over_current_change = 0;
> +		break;
> +	default:
> +		pr_err("DWC OTG HCD - ClearPortFeature request %xh "
> +		       "unknown or unsupported\n", val);
> +		spin_unlock_irqrestore(&hcd->lock, flags);
> +		return -EINVAL;
> +	}
> +	spin_unlock_irqrestore(&hcd->lock, flags);
> +	return 0;
> +}
> +
> +/* Handles the hub class-specific SetPortFeature request.*/
> +static int do_set_port_feature(struct usb_hcd *hcd, u16 val, u16 index)
> +{
> +	struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
> +	u32 hprt0 = 0;
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	unsigned long flags;
> +	u32 pcgcctl = 0;

Tabify the declarations ;

> +
> +	spin_lock_irqsave(&dwc_hcd->lock, flags);
> +
> +	switch (val) {
> +	case USB_PORT_FEAT_SUSPEND:
> +		if (hcd->self.otg_port == index && hcd->self.b_hnp_enable)
{
> +			u32 gotgctl = 0;
> +			gotgctl |= DWC_GCTL_HOST_HNP_ENA;
> +			dwc_modify32(core_if->core_global_regs +
> +				     DWC_GOTGCTL, 0, gotgctl);
> +			core_if->xceiv->state = OTG_STATE_A_SUSPEND;
> +		}
> +
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_SUS_RW(hprt0, 1);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +
> +		/* Suspend the Phy Clock */
> +		pcgcctl = DWC_PCGCCTL_STOP_CLK_SET(pcgcctl);
> +		dwc_write32(core_if->pcgcctl, pcgcctl);
> +
> +		/* For HNP the bus must be suspended for at least 200ms.
*/
> +		if (hcd->self.b_hnp_enable) {
> +			spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +			msleep(200);
> +			spin_lock_irqsave(&dwc_hcd->lock, flags);
> +		}
> +		break;
> +	case USB_PORT_FEAT_POWER:
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +		hprt0 = DWC_HPRT0_PRT_PWR_RW(hprt0, 1);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		break;
> +	case USB_PORT_FEAT_RESET:
> +		hprt0 = dwc_otg_read_hprt0(core_if);
> +
> +		/*
> +		 * When B-Host the Port reset bit is set in the Start HCD
> +		 * Callback function, so that the reset is started within
1ms
> +		 * of the HNP success interrupt.
> +		 */
> +		if (!hcd->self.is_b_host) {
> +			hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 1);
> +			dwc_write32(core_if->host_if->hprt0, hprt0);
> +		}
> +
> +		/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
> +		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +		msleep(60);
> +		spin_lock_irqsave(&dwc_hcd->lock, flags);
> +		hprt0 = DWC_HPRT0_PRT_RST_RW(hprt0, 0);
> +		dwc_write32(core_if->host_if->hprt0, hprt0);
> +		break;
> +	case USB_PORT_FEAT_INDICATOR:
> +		/* Not supported */
> +		break;
> +	default:
> +		pr_err("DWC OTG HCD - "
> +		       "SetPortFeature request %xh "
> +		       "unknown or unsupported\n", val);
> +		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +		return -EINVAL;
> +	}
> +	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +	return 0;
> +}
> +
> +/* Handles hub class-specific requests.*/
> +static int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, u16 req_type,
u16 val,
> +				   u16 index, char *buf, u16 len)
> +{
> +	int retval = 0;
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	struct core_if *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if;
> +	struct usb_hub_descriptor *desc;
> +	u32 hprt0 = 0;
> +	u32 port_status;
> +	unsigned long flags;

Tabify the declarations ;

> +
> +	spin_lock_irqsave(&dwc_hcd->lock, flags);
> +	switch (req_type) {
> +	case ClearHubFeature:
> +		switch (val) {
> +		case C_HUB_LOCAL_POWER:
> +		case C_HUB_OVER_CURRENT:
> +			/* Nothing required here */
> +			break;
> +		default:
> +			retval = -EINVAL;
> +			pr_err("DWC OTG HCD - ClearHubFeature request"
> +			       " %xh unknown\n", val);
> +		}
> +		break;
> +	case ClearPortFeature:
> +		if (!index || index > 1)
> +			goto error;
> +		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +		retval = do_clear_port_feature(dwc_hcd, val);
> +		spin_lock_irqsave(&dwc_hcd->lock, flags);
> +		break;
> +	case GetHubDescriptor:
> +		desc = (struct usb_hub_descriptor *)buf;
> +		desc->bDescLength = 9;
> +		desc->bDescriptorType = 0x29;
> +		desc->bNbrPorts = 1;
> +		desc->wHubCharacteristics = 0x08;
> +		desc->bPwrOn2PwrGood = 1;
> +		desc->bHubContrCurrent = 0;
> +		break;
> +	case GetHubStatus:
> +		memset(buf, 0, 4);
> +		break;
> +	case GetPortStatus:
> +		if (!index || index > 1)
> +			goto error;
> +
> +		port_status = 0;
> +		if (dwc_hcd->flags.b.port_connect_status_change)
> +			port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
> +		if (dwc_hcd->flags.b.port_enable_change)
> +			port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
> +		if (dwc_hcd->flags.b.port_suspend_change)
> +			port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
> +		if (dwc_hcd->flags.b.port_reset_change)
> +			port_status |= (1 << USB_PORT_FEAT_C_RESET);
> +		if (dwc_hcd->flags.b.port_over_current_change) {
> +			pr_err("Device Not Supported\n");
> +			port_status |= (1 <<
USB_PORT_FEAT_C_OVER_CURRENT);
> +		}
> +		if (!dwc_hcd->flags.b.port_connect_status) {
> +			/*
> +			 * The port is disconnected, which means the core
is
> +			 * either in device mode or it soon will be. Just
> +			 * return 0's for the remainder of the port status
> +			 * since the port register can't be read if the
core
> +			 * is in device mode.
> +			 */
> +			*((__le32 *) buf) = cpu_to_le32(port_status);
> +			break;
> +		}
> +
> +		hprt0 = dwc_read32(core_if->host_if->hprt0);
> +
> +		if (DWC_HPRT0_PRT_STS_RD(hprt0))
> +			port_status |= USB_PORT_STAT_CONNECTION;
> +		if (DWC_HPRT0_PRT_ENA_RD(hprt0))
> +			port_status |= USB_PORT_STAT_ENABLE;
> +		if (DWC_HPRT0_PRT_SUS_RD(hprt0))
> +			port_status |= USB_PORT_STAT_SUSPEND;
> +		if (DWC_HPRT0_PRT_OVRCURR_ACT_RD(hprt0))
> +			port_status |= USB_PORT_STAT_OVERCURRENT;
> +		if (DWC_HPRT0_PRT_RST_RD(hprt0))
> +			port_status |= USB_PORT_STAT_RESET;
> +		if (DWC_HPRT0_PRT_PWR_RD(hprt0))
> +			port_status |= USB_PORT_STAT_POWER;
> +
> +		if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
DWC_HPRT0_PRTSPD_HIGH_SPEED)
> +			port_status |= USB_PORT_STAT_HIGH_SPEED;
> +		else if (DWC_HPRT0_PRT_SPD_RD(hprt0) ==
> +			 DWC_HPRT0_PRTSPD_LOW_SPEED)
> +			port_status |= USB_PORT_STAT_LOW_SPEED;
> +
> +		if (DWC_HPRT0_PRT_TST_CTL_RD(hprt0))
> +			port_status |= (1 << USB_PORT_FEAT_TEST);
> +
> +		/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
> +		*((__le32 *) buf) = cpu_to_le32(port_status);
> +		break;
> +	case SetHubFeature:
> +		/* No HUB features supported */
> +		break;
> +	case SetPortFeature:
> +		if (val != USB_PORT_FEAT_TEST && (!index || index > 1))
> +			goto error;
> +
> +		if (!dwc_hcd->flags.b.port_connect_status) {
> +			/*
> +			 * The port is disconnected, which means the core
is
> +			 * either in device mode or it soon will be. Just
> +			 * return without doing anything since the port
> +			 * register can't be written if the core is in
device
> +			 * mode.
> +			 */
> +			break;
> +		}
> +		spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +		retval = do_set_port_feature(hcd, val, index);
> +		spin_lock_irqsave(&dwc_hcd->lock, flags);
> +		break;
> +	default:
> +error:
> +		retval = -EINVAL;
> +		pr_warning("DWC OTG HCD - Unknown hub control request"
> +			   " type or invalid req_type: %xh index: %xh "
> +			   "val: %xh\n", req_type, index, val);
> +		break;
> +	}
> +	spin_unlock_irqrestore(&dwc_hcd->lock, flags);
> +	return retval;
> +}
> +
> +/**
> + * Handles host mode interrupts for the DWC_otg controller. Returns
IRQ_NONE if
> + * there was no interrupt to handle. Returns IRQ_HANDLED if there was a
valid
> + * interrupt.
> + *
> + * This function is called by the USB core when an interrupt occurs
> + */
> +static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +
> +	return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_hcd));
> +}
> +
> +static const struct hc_driver dwc_otg_hc_driver = {
> +	.description = dwc_otg_hcd_name,
> +	.product_desc = "DWC OTG Controller",
> +	.hcd_priv_size = sizeof(struct dwc_hcd),
> +	.irq = dwc_otg_hcd_irq,
> +	.flags = HCD_MEMORY | HCD_USB2,
> +	.start = dwc_otg_hcd_start,
> +	.stop = dwc_otg_hcd_stop,
> +	.urb_enqueue = dwc_otg_hcd_urb_enqueue,
> +	.urb_dequeue = dwc_otg_hcd_urb_dequeue,
> +	.endpoint_disable = dwc_otg_hcd_endpoint_disable,
> +	.get_frame_number = dwc_otg_hcd_get_frame_number,
> +	.hub_status_data = dwc_otg_hcd_hub_status_data,
> +	.hub_control = dwc_otg_hcd_hub_control,
> +};
> +
> +/**
> + * Frees secondary storage associated with the dwc_hcd structure
contained
> + * in the struct usb_hcd field.
> + */
> +static void dwc_otg_hcd_free(struct usb_hcd *hcd)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	u32 i;
> +
> +	del_timers(dwc_hcd);
> +
> +	/* Free memory for QH/QTD lists */
> +	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_inactive);
> +	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_deferred);
> +	qh_list_free(dwc_hcd, &dwc_hcd->non_periodic_sched_active);
> +	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_inactive);
> +	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_ready);
> +	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_assigned);
> +	qh_list_free(dwc_hcd, &dwc_hcd->periodic_sched_queued);
> +
> +	/* Free memory for the host channels. */
> +	for (i = 0; i < MAX_EPS_CHANNELS; i++) {
> +		struct dwc_hc *hc = dwc_hcd->hc_ptr_array[i];
> +
> +		kfree(hc);
> +	}
> +	if (dwc_hcd->core_if->dma_enable) {
> +		if (dwc_hcd->status_buf_dma)
> +			dma_free_coherent(hcd->self.controller,
> +					  DWC_OTG_HCD_STATUS_BUF_SIZE,
> +					  dwc_hcd->status_buf,
> +					  dwc_hcd->status_buf_dma);
> +	} else {
> +		kfree(dwc_hcd->status_buf);
> +	}
> +
> +}
> +
> +/**
> + * Initializes the HCD. This function allocates memory for and
initializes the
> + * static parts of the usb_hcd and dwc_hcd structures. It also
registers the
> + * USB bus with the core and calls the hc_driver->start() function. It
returns
> + * a negative error on failure.
> + */
> +int __devinit dwc_otg_hcd_init(struct device *_dev,
> +			       struct dwc_otg_device *dwc_otg_device)
> +{
> +	struct usb_hcd *hcd;
> +	struct dwc_hcd *dwc_hcd;
> +	struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
> +	int num_channels;
> +	u32 i;
> +	struct dwc_hc *channel;
> +	int retval = 0;

Tabify the declarations ;

> +
> +	/*
> +	 * Allocate memory for the base HCD plus the DWC OTG HCD.
> +	 * Initialize the base HCD.
> +	 */
> +	hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dwc_otg_hcd_name);
> +	if (!hcd) {
> +		retval = -ENOMEM;
> +		goto error1;
> +	}
> +	dev_set_drvdata(_dev, dwc_otg_device);
> +	hcd->regs = otg_dev->base;
> +	hcd->rsrc_start = otg_dev->phys_addr;
> +	hcd->rsrc_len = otg_dev->base_len;
> +	hcd->self.otg_port = 1;
> +
> +	/* Initialize the DWC OTG HCD. */
> +	dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	dwc_hcd->core_if = otg_dev->core_if;
> +	spin_lock_init(&dwc_hcd->lock);
> +	otg_dev->hcd = dwc_hcd;
> +
> +	/* Register the HCD CIL Callbacks */
> +	dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if,
&hcd_cil_callbacks,
> +					   hcd);
> +
> +	/* Initialize the non-periodic schedule. */
> +	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_inactive);
> +	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_active);
> +	INIT_LIST_HEAD(&dwc_hcd->non_periodic_sched_deferred);
> +
> +	/* Initialize the periodic schedule. */
> +	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_inactive);
> +	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_ready);
> +	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_assigned);
> +	INIT_LIST_HEAD(&dwc_hcd->periodic_sched_queued);
> +
> +	/*
> +	 * Create a host channel descriptor for each host channel
implemented
> +	 * in the controller. Initialize the channel descriptor array.
> +	 */
> +	INIT_LIST_HEAD(&dwc_hcd->free_hc_list);
> +	num_channels = dwc_hcd->core_if->core_params->host_channels;
> +
> +	for (i = 0; i < num_channels; i++) {
> +		channel = kzalloc(sizeof(struct dwc_hc), GFP_KERNEL);
> +		if (!channel) {
> +			retval = -ENOMEM;
> +			pr_err("%s: host channel allocation failed\n",
> +			       __func__);
> +			goto error2;
> +		}
> +
> +		channel->hc_num = i;
> +		dwc_hcd->hc_ptr_array[i] = channel;
> +	}
> +
> +	/* Initialize the Connection timeout timer. */
> +	init_timer(&dwc_hcd->conn_timer);
> +
> +	/* Initialize workqueue */
> +	INIT_WORK(&dwc_hcd->usb_port_reset, port_reset_wqfunc);
> +	INIT_WORK(&dwc_hcd->start_work, hcd_start_func);
> +	INIT_WORK(&dwc_hcd->core_if->usb_port_otg, NULL);
> +	INIT_DELAYED_WORK(&dwc_hcd->core_if->usb_port_wakeup,
> +			  port_wakeup_wqfunc);
> +
> +	/* Set device flags indicating whether the HCD supports DMA. */
> +	if (otg_dev->core_if->dma_enable) {
> +		static u64 dummy_mask = DMA_BIT_MASK(32);
> +
> +		pr_info("Using DMA mode\n");
> +		_dev->dma_mask = (void *)&dummy_mask;
> +		_dev->coherent_dma_mask = ~0;
> +	} else {
> +		pr_info("Using Slave mode\n");
> +		_dev->dma_mask = (void *)0;
> +		_dev->coherent_dma_mask = 0;
> +	}
> +
> +	init_hcd_usecs(dwc_hcd);
> +	/*
> +	 * Finish generic HCD initialization and start the HCD. This
function
> +	 * allocates the DMA buffer pool, registers the USB bus, requests
the
> +	 * IRQ line, and calls dwc_otg_hcd_start method.
> +	 */
> +	retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED);
> +	if (retval < 0)
> +		goto error2;
> +	hcd->rsrc_start = otg_dev->phys_addr;
> +	hcd->rsrc_len = otg_dev->base_len;
> +
> +	/*
> +	 * Allocate space for storing data on status transactions.
Normally no
> +	 * data is sent, but this space acts as a bit bucket. This must be
> +	 * done after usb_add_hcd since that function allocates the DMA
buffer
> +	 * pool.
> +	 */
> +	if (otg_dev->core_if->dma_enable) {
> +		dwc_hcd->status_buf =
> +		    dma_alloc_coherent(_dev, DWC_OTG_HCD_STATUS_BUF_SIZE,
> +				       &dwc_hcd->status_buf_dma,
> +				       GFP_KERNEL | GFP_DMA);
> +	} else {
> +		dwc_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE,
> +					      GFP_KERNEL);
> +	}
> +	if (!dwc_hcd->status_buf) {
> +		retval = -ENOMEM;
> +		pr_err("%s: status_buf allocation failed\n", __func__);
> +		goto error3;
> +	}
> +	return 0;
> +
> +error3:
> +	usb_remove_hcd(hcd);
> +error2:
> +	dwc_otg_hcd_free(hcd);
> +	usb_put_hcd(hcd);
> +error1:
> +	return retval;
> +}
> +
> +/**
> + * Removes the HCD.
> + * Frees memory and resources associated with the HCD and deregisters
the bus.
> + */
> +void __devexit dwc_otg_hcd_remove(struct device *_dev)
> +{
> +	struct dwc_otg_device *otg_dev = dev_get_drvdata(_dev);
> +	struct dwc_hcd *dwc_hcd = otg_dev->hcd;
> +	struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_hcd);
> +
> +	/* Turn off all interrupts */
> +	dwc_write32(gintmsk_reg(dwc_hcd), 0);
> +	dwc_modify32(gahbcfg_reg(dwc_hcd), 1, 0);
> +
> +	cancel_work_sync(&dwc_hcd->start_work);
> +	cancel_work_sync(&dwc_hcd->usb_port_reset);
> +	cancel_work_sync(&dwc_hcd->core_if->usb_port_otg);
> +
> +	usb_remove_hcd(hcd);
> +	dwc_otg_hcd_free(hcd);
> +	usb_put_hcd(hcd);
> +}
> +
> +/** Returns the current frame number. */
> +int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd)
> +{
> +	struct dwc_hcd *dwc_hcd = hcd_to_dwc_otg_hcd(hcd);
> +	u32 hfnum = 0;
> +
> +	hfnum = dwc_read32(dwc_hcd->core_if->host_if->
> +			   host_global_regs + DWC_HFNUM);
> +
> +	return DWC_HFNUM_FRNUM_RD(hfnum);
> +}
> +
> +/**
> + * Prepares a host channel for transferring packets to/from a specific
> + * endpoint. The HCCHARn register is set up with the characteristics
specified
> + * in _hc. Host channel interrupts that may need to be serviced while
this
> + * transfer is in progress are enabled.
> + */
> +static void dwc_otg_hc_init(struct core_if *core_if, struct dwc_hc *hc)
> +{
> +	u32 intr_enable;
> +	ulong global_regs = core_if->core_global_regs;
> +	u32 hc_intr_mask = 0;
> +	u32 gintmsk = 0;
> +	u32 hcchar;
> +	u32 hcsplt;
> +	u8 hc_num = hc->hc_num;
> +	struct dwc_host_if *host_if = core_if->host_if;
> +	ulong hc_regs = host_if->hc_regs[hc_num];

Tabify the declarations ;

> +
> +	/* Clear old interrupt conditions for this host channel. */
> +	hc_intr_mask = 0x3FF;
> +	dwc_write32(hc_regs + DWC_HCINT, hc_intr_mask);
> +
> +	/* Enable channel interrupts required for this transfer. */
> +	hc_intr_mask = 0;
> +	hc_intr_mask = DWC_HCINTMSK_CHAN_HALTED_RW(hc_intr_mask, 1);
> +	if (core_if->dma_enable) {
> +		hc_intr_mask = DWC_HCINTMSK_AHB_ERR_RW(hc_intr_mask, 1);
> +
> +		if (hc->error_state && !hc->do_split &&
> +		    hc->ep_type != DWC_OTG_EP_TYPE_ISOC) {
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
> +			if (hc->ep_is_in) {
> +				hc_intr_mask =
> +
DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask,
> +								 1);
> +				if (hc->ep_type != DWC_OTG_EP_TYPE_INTR)
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_NAK_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +			}
> +		}
> +	} else {
> +		switch (hc->ep_type) {
> +		case DWC_OTG_EP_TYPE_CONTROL:
> +		case DWC_OTG_EP_TYPE_BULK:
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask,
1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
> +
> +			if (hc->ep_is_in) {
> +				hc_intr_mask =
> +				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> +			} else {
> +				hc_intr_mask =
> +
DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
> +								 1);
> +				hc_intr_mask =
> +
DWC_HCINTMSK_NYET_RESP_REC_RW(hc_intr_mask,
> +								  1);
> +				if (hc->do_ping)
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_ACK_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +			}
> +
> +			if (hc->do_split) {
> +				hc_intr_mask =
> +
DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask,
> +								 1);
> +				if (hc->complete_split)
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_NYET_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +				else
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_ACK_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +			}
> +
> +			if (hc->error_state)
> +				hc_intr_mask =
> +
DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
> +								 1);
> +			break;
> +		case DWC_OTG_EP_TYPE_INTR:
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_NAK_RESP_REC_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_STALL_RESP_REC_RW(hc_intr_mask,
1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_DATA_TOG_ERR_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask,
1);
> +
> +			if (hc->ep_is_in)
> +				hc_intr_mask =
> +				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> +			if (hc->error_state)
> +				hc_intr_mask =
> +
DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask,
> +								 1);
> +
> +			if (hc->do_split) {
> +				if (hc->complete_split)
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_NYET_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +				else
> +					hc_intr_mask =
> +					    DWC_HCINTMSK_ACK_RESP_REC_RW
> +					    (hc_intr_mask, 1);
> +			}
> +			break;
> +		case DWC_OTG_EP_TYPE_ISOC:
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_TXFER_CMPL_RW(hc_intr_mask, 1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_FRAME_OVERN_ERR_RW(hc_intr_mask,
1);
> +			hc_intr_mask =
> +			    DWC_HCINTMSK_ACK_RESP_REC_RW(hc_intr_mask, 1);
> +
> +			if (hc->ep_is_in) {
> +				hc_intr_mask =
> +
DWC_HCINTMSK_TRANS_ERR_RW(hc_intr_mask, 1);
> +				hc_intr_mask =
> +				    DWC_HCINTMSK_BBL_ERR_RW(hc_intr_mask,
1);
> +			}
> +			break;
> +		}
> +	}
> +	dwc_write32(hc_regs + DWC_HCINTMSK, hc_intr_mask);
> +
> +	/* Enable the top level host channel interrupt. */
> +	intr_enable = (1 << hc_num);
> +	dwc_modify32(host_if->host_global_regs + DWC_HAINTMSK, 0,
> +		     intr_enable);
> +
> +	/* Make sure host channel interrupts are enabled. */
> +	gintmsk |= DWC_INTMSK_HST_CHAN;
> +	dwc_modify32(global_regs + DWC_GINTMSK, 0, gintmsk);
> +
> +	/*
> +	 * Program the HCCHARn register with the endpoint characteristics
for
> +	 * the current transfer.
> +	 */
> +	hcchar = 0;
> +	hcchar = DWC_HCCHAR_DEV_ADDR_RW(hcchar, hc->dev_addr);
> +	hcchar = DWC_HCCHAR_EP_NUM_RW(hcchar, hc->ep_num);
> +	hcchar = DWC_HCCHAR_EPDIR_RW(hcchar, hc->ep_is_in);
> +	hcchar = DWC_HCCHAR_LSP_DEV_RW(hcchar, (hc->speed ==
> +						DWC_OTG_EP_SPEED_LOW));
> +	hcchar = DWC_HCCHAR_EPTYPE_RW(hcchar, hc->ep_type);
> +	hcchar = DWC_HCCHAR_MPS_RW(hcchar, hc->max_packet);
> +	dwc_write32(host_if->hc_regs[hc_num] + DWC_HCCHAR, hcchar);
> +
> +	/* Program the HCSPLIT register for SPLITs */
> +	hcsplt = 0;
> +	if (hc->do_split) {
> +		hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt,
hc->complete_split);
> +		hcsplt = DWC_HCSPLT_TRANS_POS_RW(hcsplt, hc->xact_pos);
> +		hcsplt = DWC_HCSPLT_HUB_ADDR_RW(hcsplt, hc->hub_addr);
> +		hcsplt = DWC_HCSPLT_PRT_ADDR_RW(hcsplt, hc->port_addr);
> +	}
> +	dwc_write32(host_if->hc_regs[hc_num] + DWC_HCSPLT, hcsplt);
> +}
> +
> +/**
> + * Assigns transactions from a QTD to a free host channel and
initializes the
> + * host channel to perform the transactions. The host channel is
removed from
> + * the free list.
> + */
> +static void assign_and_init_hc(struct dwc_hcd *hcd, struct dwc_qh *qh)
> +{
> +	struct dwc_hc *hc;
> +	struct dwc_qtd *qtd;
> +	struct urb *urb;
> +	struct usb_iso_packet_descriptor *frame_desc;
> +
> +	hc = list_entry(hcd->free_hc_list.next, struct dwc_hc,
hc_list_entry);
> +
> +	/* Remove the host channel from the free list. */
> +	list_del_init(&hc->hc_list_entry);
> +	qtd = list_entry(qh->qtd_list.next, struct dwc_qtd,
qtd_list_entry);
> +	urb = qtd->urb;
> +	qh->channel = hc;
> +	qh->qtd_in_process = qtd;
> +
> +	/*
> +	 * Use usb_pipedevice to determine device address. This address is
> +	 * 0 before the SET_ADDRESS command and the correct address
afterward.
> +	 */
> +	hc->dev_addr = usb_pipedevice(urb->pipe);
> +	hc->ep_num = usb_pipeendpoint(urb->pipe);
> +
> +	if (urb->dev->speed == USB_SPEED_LOW)
> +		hc->speed = DWC_OTG_EP_SPEED_LOW;
> +	else if (urb->dev->speed == USB_SPEED_FULL)
> +		hc->speed = DWC_OTG_EP_SPEED_FULL;
> +	else
> +		hc->speed = DWC_OTG_EP_SPEED_HIGH;
> +
> +	hc->max_packet = dwc_max_packet(qh->maxp);
> +	hc->xfer_started = 0;
> +	hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS;
> +	hc->error_state = (qtd->error_count > 0);
> +	hc->halt_on_queue = 0;
> +	hc->halt_pending = 0;
> +	hc->requests = 0;
> +
> +	/*
> +	 * The following values may be modified in the transfer type
section
> +	 * below. The xfer_len value may be reduced when the transfer is
> +	 * started to accommodate the max widths of the XferSize and
PktCnt
> +	 * fields in the HCTSIZn register.
> +	 */
> +	hc->do_ping = qh->ping_state;
> +	hc->ep_is_in = (usb_pipein(urb->pipe) != 0);
> +	hc->data_pid_start = qh->data_toggle;
> +	hc->multi_count = 1;
> +
> +	if (hcd->core_if->dma_enable)
> +		hc->xfer_buff = urb->transfer_dma + (u8 *)
urb->actual_length;
> +	else
> +		hc->xfer_buff = (u8 *) urb->transfer_buffer +
> +		    urb->actual_length;
> +
> +	hc->xfer_len = urb->transfer_buffer_length - urb->actual_length;
> +	hc->xfer_count = 0;
> +
> +	/*
> +	 * Set the split attributes
> +	 */
> +	hc->do_split = 0;
> +	if (qh->do_split) {
> +		hc->do_split = 1;
> +		hc->xact_pos = qtd->isoc_split_pos;
> +		hc->complete_split = qtd->complete_split;
> +		hc->hub_addr = urb->dev->tt->hub->devnum;
> +		hc->port_addr = urb->dev->ttport;
> +	}
> +
> +	switch (usb_pipetype(urb->pipe)) {
> +	case PIPE_CONTROL:
> +		hc->ep_type = DWC_OTG_EP_TYPE_CONTROL;
> +
> +		switch (qtd->control_phase) {
> +		case DWC_OTG_CONTROL_SETUP:
> +			hc->do_ping = 0;
> +			hc->ep_is_in = 0;
> +			hc->data_pid_start = DWC_OTG_HC_PID_SETUP;
> +
> +			if (hcd->core_if->dma_enable)
> +				hc->xfer_buff = (u8 *) (u32)
urb->setup_dma;
> +			else
> +				hc->xfer_buff = (u8 *) urb->setup_packet;
> +
> +			hc->xfer_len = 8;
> +			break;
> +		case DWC_OTG_CONTROL_DATA:
> +			hc->data_pid_start = qtd->data_toggle;
> +			break;
> +		case DWC_OTG_CONTROL_STATUS:
> +			/*
> +			 * Direction is opposite of data direction or IN
if no
> +			 * data.
> +			 */
> +			if (urb->transfer_buffer_length == 0)
> +				hc->ep_is_in = 1;
> +			else
> +				hc->ep_is_in = (usb_pipein(urb->pipe) !=
> +						USB_DIR_IN);
> +
> +			if (hc->ep_is_in)
> +				hc->do_ping = 0;
> +
> +			hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
> +			hc->xfer_len = 0;
> +			if (hcd->core_if->dma_enable)
> +				hc->xfer_buff =
> +				    (u8 *) (u32) hcd->status_buf_dma;
> +			else
> +				hc->xfer_buff = (u8 *) hcd->status_buf;
> +			break;
> +		}
> +		break;
> +	case PIPE_BULK:
> +		hc->ep_type = DWC_OTG_EP_TYPE_BULK;
> +		break;
> +	case PIPE_INTERRUPT:
> +		hc->ep_type = DWC_OTG_EP_TYPE_INTR;
> +		break;
> +	case PIPE_ISOCHRONOUS:
> +		frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index];
> +		hc->ep_type = DWC_OTG_EP_TYPE_ISOC;
> +
> +		if (hcd->core_if->dma_enable)
> +			hc->xfer_buff = (u8 *) (u32) urb->transfer_dma;
> +		else
> +			hc->xfer_buff = (u8 *) urb->transfer_buffer;
> +
> +		hc->xfer_buff += frame_desc->offset +
qtd->isoc_split_offset;
> +		hc->xfer_len = frame_desc->length -
qtd->isoc_split_offset;
> +
> +		if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) {
> +			if (hc->xfer_len <= 188)
> +				hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL;
> +			else
> +				hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN;
> +		}
> +		break;
> +	}
> +
> +	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> +	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> +		/*
> +		 * This value may be modified when the transfer is started
to
> +		 * reflect the actual transfer length.
> +		 */
> +		hc->multi_count = dwc_hb_mult(qh->maxp);
> +
> +	dwc_otg_hc_init(hcd->core_if, hc);
> +	hc->qh = qh;
> +}
> +
> +/**
> + * This function selects transactions from the HCD transfer schedule
and
> + * assigns them to available host channels. It is called from HCD
interrupt
> + * handler functions.
> + */
> +enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct
dwc_hcd *hcd)
> +{
> +	struct list_head *qh_ptr;
> +	struct dwc_qh *qh;
> +	int num_channels;
> +	enum dwc_transaction_type ret_val = DWC_OTG_TRANSACTION_NONE;
> +
> +	/* Process entries in the periodic ready list. */
> +	num_channels = hcd->core_if->core_params->host_channels;
> +	qh_ptr = hcd->periodic_sched_ready.next;
> +	while (qh_ptr != &hcd->periodic_sched_ready &&
> +	       !list_empty(&hcd->free_hc_list)) {
> +		/* Leave one channel for non periodic transactions. */
> +		if (hcd->available_host_channels <= 1)
> +			break;
> +		hcd->available_host_channels--;
> +		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> +		assign_and_init_hc(hcd, qh);
> +		/*
> +		 * Move the QH from the periodic ready schedule to the
> +		 * periodic assigned schedule.
> +		 */
> +		qh_ptr = qh_ptr->next;
> +		list_move(&qh->qh_list_entry,
&hcd->periodic_sched_assigned);
> +		ret_val = DWC_OTG_TRANSACTION_PERIODIC;
> +	}
> +
> +	/*
> +	 * Process entries in the deferred portion of the non-periodic
list.
> +	 * A NAK put them here and, at the right time, they need to be
> +	 * placed on the sched_inactive list.
> +	 */
> +	qh_ptr = hcd->non_periodic_sched_deferred.next;
> +	while (qh_ptr != &hcd->non_periodic_sched_deferred) {
> +		u16 frame_number =
> +		    dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd));
> +		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> +		qh_ptr = qh_ptr->next;
> +
> +		if (dwc_frame_num_le(qh->sched_frame, frame_number))
> +			/*
> +			 * Move the QH from the non periodic deferred
schedule
> +			 * to the non periodic inactive schedule.
> +			 */
> +			list_move(&qh->qh_list_entry,
> +				  &hcd->non_periodic_sched_inactive);
> +	}
> +
> +	/*
> +	 * Process entries in the inactive portion of the non-periodic
> +	 * schedule. Some free host channels may not be used if they are
> +	 * reserved for periodic transfers.
> +	 */
> +	qh_ptr = hcd->non_periodic_sched_inactive.next;
> +	num_channels = hcd->core_if->core_params->host_channels;
> +
> +	while (qh_ptr != &hcd->non_periodic_sched_inactive
> +	       && !list_empty(&hcd->free_hc_list)) {
> +		if (hcd->available_host_channels < 1)
> +			break;
> +		hcd->available_host_channels--;
> +		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> +		assign_and_init_hc(hcd, qh);
> +		/*
> +		 * Move the QH from the non-periodic inactive schedule to
the
> +		 * non-periodic active schedule.
> +		 */
> +		qh_ptr = qh_ptr->next;
> +		list_move(&qh->qh_list_entry,
&hcd->non_periodic_sched_active);
> +		if (ret_val == DWC_OTG_TRANSACTION_NONE)
> +			ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC;
> +		else
> +			ret_val = DWC_OTG_TRANSACTION_ALL;
> +
> +	}
> +	return ret_val;
> +}
> +
> +/**
> + * Sets the channel property that indicates in which frame a periodic
transfer
> + * should occur. This is always set to the _next_ frame. This function
has no
> + * effect on non-periodic transfers.
> + */
> +static inline void hc_set_even_odd_frame(struct core_if *core_if,
> +					 struct dwc_hc *hc, u32 * hcchar)
> +{
> +	if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> +	    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> +		u32 hfnum = 0;
> +
> +		hfnum = dwc_read32(core_if->host_if->host_global_regs +
> +				   DWC_HFNUM);
> +
> +		/* 1 if _next_ frame is odd, 0 if it's even */
> +		*hcchar = DWC_HCCHAR_ODD_FRAME_RW(*hcchar,
> +
((DWC_HFNUM_FRNUM_RD(hfnum) &
> +						    0x1) ? 0 : 1));
> +	}
> +}
> +
> +static void set_initial_xfer_pid(struct dwc_hc *hc)
> +{
> +	if (hc->speed == DWC_OTG_EP_SPEED_HIGH) {
> +		if (hc->ep_is_in) {
> +			if (hc->multi_count == 1)
> +				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> +			else if (hc->multi_count == 2)
> +				hc->data_pid_start = DWC_OTG_HC_PID_DATA1;
> +			else
> +				hc->data_pid_start = DWC_OTG_HC_PID_DATA2;
> +		} else {
> +			if (hc->multi_count == 1)
> +				hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> +			else
> +				hc->data_pid_start = DWC_OTG_HC_PID_MDATA;
> +		}
> +	} else {
> +		hc->data_pid_start = DWC_OTG_HC_PID_DATA0;
> +	}
> +}
> +
> +/**
> + * Starts a PING transfer. This function should only be called in Slave
mode.
> + * The Do Ping bit is set in the HCTSIZ register, then the channel is
enabled.
> + */
> +static void dwc_otg_hc_do_ping(struct core_if *core_if, struct dwc_hc
*hc)
> +{
> +	u32 hcchar;
> +	u32 hctsiz = 0;
> +
> +	ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +
> +	hctsiz = 0;
> +	hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
> +	hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, 1);
> +	dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
> +
> +	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> +	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> +	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +}
> +
> +/**
> + * This function writes a packet into the Tx FIFO associated with the
Host
> + * Channel. For a channel associated with a non-periodic EP, the
non-periodic
> + * Tx FIFO is written. For a channel associated with a periodic EP, the
> + * periodic Tx FIFO is written. This function should only be called in
Slave
> + * mode.
> + *
> + * Upon return the xfer_buff and xfer_count fields in hc are
incremented by
> + * then number of bytes written to the Tx FIFO.
> + */
> +static void dwc_otg_hc_write_packet(struct core_if *core_if, struct
dwc_hc *hc)
> +{
> +	u32 i;
> +	u32 remaining_count;
> +	u32 byte_count;
> +	u32 dword_count;
> +	u32 *data_buff = (u32 *) (hc->xfer_buff);
> +	u32 data_fifo = core_if->data_fifo[hc->hc_num];
> +
> +	remaining_count = hc->xfer_len - hc->xfer_count;
> +	if (remaining_count > hc->max_packet)
> +		byte_count = hc->max_packet;
> +	else
> +		byte_count = remaining_count;
> +
> +	dword_count = (byte_count + 3) / 4;
> +
> +	if (((unsigned long)data_buff) & 0x3)
> +		/* xfer_buff is not DWORD aligned. */
> +		for (i = 0; i < dword_count; i++, data_buff++)
> +			dwc_write_fifo32(data_fifo,
> +					     get_unaligned(data_buff));
> +	else
> +		/* xfer_buff is DWORD aligned. */
> +		for (i = 0; i < dword_count; i++, data_buff++)
> +			dwc_write_fifo32(data_fifo, *data_buff);
> +
> +	hc->xfer_count += byte_count;
> +	hc->xfer_buff += byte_count;
> +}
> +
> +/**
> + * This function does the setup for a data transfer for a host channel
and
> + * starts the transfer. May be called in either Slave mode or DMA mode.
In
> + * Slave mode, the caller must ensure that there is sufficient space in
the
> + * request queue and Tx Data FIFO.
> + *
> + * For an OUT transfer in Slave mode, it loads a data packet into the
> + * appropriate FIFO. If necessary, additional data packets will be
loaded in
> + * the Host ISR.
> + *
> + * For an IN transfer in Slave mode, a data packet is requested. The
data
> + * packets are unloaded from the Rx FIFO in the Host ISR. If necessary,
> + * additional data packets are requested in the Host ISR.
> + *
> + * For a PING transfer in Slave mode, the Do Ping bit is set in the
HCTSIZ
> + * register along with a packet count of 1 and the channel is enabled.
This
> + * causes a single PING transaction to occur. Other fields in HCTSIZ
are
> + * simply set to 0 since no data transfer occurs in this case.
> + *
> + * For a PING transfer in DMA mode, the HCTSIZ register is initialized
with
> + * all the information required to perform the subsequent data
transfer. In
> + * addition, the Do Ping bit is set in the HCTSIZ register. In this
case, the
> + * controller performs the entire PING protocol, then starts the data
> + * transfer.
> + */
> +static void dwc_otg_hc_start_transfer(struct core_if *core_if,
> +				      struct dwc_hc *hc)
> +{
> +	u32 hcchar;
> +	u32 hctsiz = 0;
> +	u16 num_packets;
> +	u32 max_hc_xfer_size = core_if->core_params->max_transfer_size;
> +	u16 max_hc_pkt_count = core_if->core_params->max_packet_count;
> +	ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +	hctsiz = 0;
> +
> +	if (hc->do_ping) {
> +		if (!core_if->dma_enable) {
> +			dwc_otg_hc_do_ping(core_if, hc);
> +			hc->xfer_started = 1;
> +			return;
> +		} else {
> +			hctsiz = DWC_HCTSIZ_DO_PING_PROTO_RW(hctsiz, 1);
> +		}
> +	}
> +
> +	if (hc->do_split) {
> +		num_packets = 1;
> +
> +		if (hc->complete_split && !hc->ep_is_in)
> +			/*
> +			 * For CSPLIT OUT Transfer, set the size to 0 so
the
> +			 * core doesn't expect any data written to the
FIFO
> +			 */
> +			hc->xfer_len = 0;
> +		else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet))
> +			hc->xfer_len = hc->max_packet;
> +		else if (!hc->ep_is_in && (hc->xfer_len > 188))
> +			hc->xfer_len = 188;
> +
> +		hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
> +	} else {
> +		/*
> +		 * Ensure that the transfer length and packet count will
fit
> +		 * in the widths allocated for them in the HCTSIZn
register.
> +		 */
> +		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> +		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> +			u32 max_len = hc->multi_count * hc->max_packet;
> +
> +			/*
> +			 * Make sure the transfer size is no larger than
one
> +			 * (micro)frame's worth of data. (A check was done
> +			 * when the periodic transfer was accepted to
ensure
> +			 * that a (micro)frame's worth of data can be
> +			 * programmed into a channel.)
> +			 */
> +			if (hc->xfer_len > max_len)
> +				hc->xfer_len = max_len;
> +		} else if (hc->xfer_len > max_hc_xfer_size) {
> +			/*
> +			 * Make sure that xfer_len is a multiple of max
packet
> +			 * size.
> +			 */
> +			hc->xfer_len = max_hc_xfer_size - hc->max_packet +
1;
> +		}
> +		if (hc->xfer_len > 0) {
> +			num_packets = (hc->xfer_len + hc->max_packet - 1)
/
> +			    hc->max_packet;
> +			if (num_packets > max_hc_pkt_count) {
> +				num_packets = max_hc_pkt_count;
> +				hc->xfer_len = num_packets *
hc->max_packet;
> +			}
> +		} else {
> +			/* Need 1 packet for transfer length of 0. */
> +			num_packets = 1;
> +		}
> +
> +		if (hc->ep_is_in)
> +			/*
> +			 * Always program an integral # of max packets for
IN
> +			 * transfers.
> +			 */
> +			hc->xfer_len = num_packets * hc->max_packet;
> +
> +		if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> +		    hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> +			/*
> +			 * Make sure that the multi_count field matches
the
> +			 * actual transfer length.
> +			 */
> +			hc->multi_count = num_packets;
> +
> +		/* Set up the initial PID for the transfer. */
> +		if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)
> +			set_initial_xfer_pid(hc);
> +
> +		hctsiz = DWC_HCTSIZ_XFER_SIZE_RW(hctsiz, hc->xfer_len);
> +	}
> +
> +	hc->start_pkt_count = num_packets;
> +	hctsiz = DWC_HCTSIZ_PKT_CNT_RW(hctsiz, num_packets);
> +	hctsiz = DWC_HCTSIZ_PKT_PID_RW(hctsiz, hc->data_pid_start);
> +	dwc_write32(hc_regs + DWC_HCTSIZ, hctsiz);
> +
> +	if (core_if->dma_enable)
> +		dwc_write32(hc_regs + DWC_HCDMA, (u32) hc->xfer_buff);
> +
> +	/* Start the split */
> +	if (hc->do_split) {
> +		u32 hcsplt;
> +
> +		hcsplt = dwc_read32(hc_regs + DWC_HCSPLT);
> +		hcsplt = DWC_HCSPLT_COMP_SPLT_RW(hcsplt, 1);
> +		dwc_write32(hc_regs + DWC_HCSPLT, hcsplt);
> +	}
> +
> +	hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +	hcchar = DWC_HCCHAR_MULTI_CNT_RW(hcchar, hc->multi_count);
> +	hc_set_even_odd_frame(core_if, hc, &hcchar);
> +
> +	/* Set host channel enable after all other setup is complete. */
> +	hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> +	hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> +	dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> +	hc->xfer_started = 1;
> +	hc->requests++;
> +	if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0)
> +		/* Load OUT packet into the appropriate Tx FIFO. */
> +		dwc_otg_hc_write_packet(core_if, hc);
> +}
> +
> +/**
> + * This function continues a data transfer that was started by previous
call
> + * to dwc_otg_hc_start_transfer</code>. The caller must ensure there is
> + * sufficient space in the request queue and Tx Data FIFO. This
function
> + * should only be called in Slave mode. In DMA mode, the controller
acts
> + * autonomously to complete transfers programmed to a host channel.
> + *
> + * For an OUT transfer, a new data packet is loaded into the
appropriate FIFO
> + * if there is any data remaining to be queued. For an IN transfer,
another
> + * data packet is always requested. For the SETUP phase of a control
transfer,
> + * this function does nothing.
> + */
> +static int dwc_otg_hc_continue_transfer(struct core_if *core_if,
> +					struct dwc_hc *hc)
> +{
> +	if (hc->do_split) {
> +		/* SPLITs always queue just once per channel */
> +		return 0;
> +	} else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) {
> +		/* SETUPs are queued only once since they can't be NAKed.
*/
> +		return 0;
> +	} else if (hc->ep_is_in) {
> +		/*
> +		 * Always queue another request for other IN transfers. If
> +		 * back-to-back INs are issued and NAKs are received for
both,
> +		 * the driver may still be processing the first NAK when
the
> +		 * second NAK is received. When the interrupt handler
clears
> +		 * the NAK interrupt for the first NAK, the second NAK
will
> +		 * not be seen. So we can't depend on the NAK interrupt
> +		 * handler to requeue a NAKed request. Instead, IN
requests
> +		 * are issued each time this function is called. When the
> +		 * transfer completes, the extra requests for the channel
will
> +		 * be flushed.
> +		 */
> +		u32 hcchar;
> +		ulong hc_regs = core_if->host_if->hc_regs[hc->hc_num];
> +
> +		hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +		hc_set_even_odd_frame(core_if, hc, &hcchar);
> +
> +		hcchar = DWC_HCCHAR_ENA_RW(hcchar, 1);
> +		hcchar = DWC_HCCHAR_DIS_RW(hcchar, 0);
> +		dwc_write32(hc_regs + DWC_HCCHAR, hcchar);
> +
> +		hc->requests++;
> +		return 1;
> +	} else {
> +		/* OUT transfers. */
> +		if (hc->xfer_count < hc->xfer_len) {
> +			if (hc->ep_type == DWC_OTG_EP_TYPE_INTR ||
> +			    hc->ep_type == DWC_OTG_EP_TYPE_ISOC) {
> +				u32 hcchar;
> +				u32 hc_regs;
> +
> +				hc_regs =
> +				    core_if->host_if->hc_regs[hc->hc_num];
> +				hcchar = dwc_read32(hc_regs + DWC_HCCHAR);
> +				hc_set_even_odd_frame(core_if, hc,
&hcchar);
> +			}
> +
> +			/* Load OUT packet into the appropriate Tx FIFO.
*/
> +			dwc_otg_hc_write_packet(core_if, hc);
> +			hc->requests++;
> +			return 1;
> +		} else {
> +			return 0;
> +		}
> +	}
> +}
> +
> +/**
> + * This function writes a packet into the Tx FIFO associated with the
Host
> + * Channel. For a channel associated with a non-periodic EP, the
non-periodic
> + * Tx FIFO is written. For a channel associated with a periodic EP, the
> + * periodic Tx FIFO is written. This function should only be called in
Slave
> + * mode.
> + *
> + * Upon return the xfer_buff and xfer_count fields in hc are
incremented by
> + * then number of bytes written to the Tx FIFO.
> + */
> +
> +/**
> + * Attempts to queue a single transaction request for a host channel
> + * associated with either a periodic or non-periodic transfer. This
function
> + * assumes that there is space available in the appropriate request
queue. For
> + * an OUT transfer or SETUP transaction in Slave mode, it checks
whether space
> + * is available in the appropriate Tx FIFO.
> + */
> +static int queue_transaction(struct dwc_hcd *hcd, struct dwc_hc *hc,
> +			     u16 _fifo_dwords_avail)
> +{
> +	int retval;
> +
> +	if (hcd->core_if->dma_enable) {
> +		if (!hc->xfer_started) {
> +			dwc_otg_hc_start_transfer(hcd->core_if, hc);
> +			hc->qh->ping_state = 0;
> +		}
> +		retval = 0;
> +	} else if (hc->halt_pending) {
> +		/* Don't queue a request if the channel has been halted.
*/
> +		retval = 0;
> +	} else if (hc->halt_on_queue) {
> +		dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status);
> +		retval = 0;
> +	} else if (hc->do_ping) {
> +		if (!hc->xfer_started)
> +			dwc_otg_hc_start_transfer(hcd->core_if, hc);
> +		retval = 0;
> +	} else if (!hc->ep_is_in || hc->data_pid_start ==
> +			DWC_OTG_HC_PID_SETUP) {
> +		if ((_fifo_dwords_avail * 4) >= hc->max_packet) {
> +			if (!hc->xfer_started) {
> +				dwc_otg_hc_start_transfer(hcd->core_if,
hc);
> +				retval = 1;
> +			} else {
> +				retval =
> +
dwc_otg_hc_continue_transfer(hcd->core_if,
> +								 hc);
> +			}
> +		} else {
> +			retval = -1;
> +		}
> +	} else {
> +		if (!hc->xfer_started) {
> +			dwc_otg_hc_start_transfer(hcd->core_if, hc);
> +			retval = 1;
> +		} else {
> +			retval =
dwc_otg_hc_continue_transfer(hcd->core_if, hc);
> +		}
> +	}
> +	return retval;
> +}
> +
> +/**
> + * Processes active non-periodic channels and queues transactions for
these
> + * channels to the DWC_otg controller. After queueing transactions, the
NP Tx
> + * FIFO Empty interrupt is enabled if there are more transactions to
queue as
> + * NP Tx FIFO or request queue space becomes available. Otherwise, the
NP Tx
> + * FIFO Empty interrupt is disabled.
> + */
> +static void process_non_periodic_channels(struct dwc_hcd *hcd)
> +{
> +	u32 tx_status = 0;
> +	struct list_head *orig_qh_ptr;
> +	struct dwc_qh *qh;
> +	int status;
> +	int no_queue_space = 0;
> +	int no_fifo_space = 0;
> +	int more_to_do = 0;
> +	ulong regs = hcd->core_if->core_global_regs;
> +
> +	/*
> +	 * Keep track of the starting point. Skip over the start-of-list
> +	 * entry.
> +	 */
> +	if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active)
> +		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
> +	orig_qh_ptr = hcd->non_periodic_qh_ptr;
> +
> +	/*
> +	 * Process once through the active list or until no more space is
> +	 * available in the request queue or the Tx FIFO.
> +	 */
> +	do {
> +		tx_status = dwc_read32(regs + DWC_GNPTXSTS);
> +		if (!hcd->core_if->dma_enable &&
> +		    DWC_GNPTXSTS_NPTXQSPCAVAIL_RD(tx_status) == 0) {
> +			no_queue_space = 1;
> +			break;
> +		}
> +
> +		qh = list_entry(hcd->non_periodic_qh_ptr, struct dwc_qh,
> +				qh_list_entry);
> +		status = queue_transaction(hcd, qh->channel,
> +					   DWC_GNPTXSTS_NPTXFSPCAVAIL_RD
> +					   (tx_status));
> +
> +		if (status > 0) {
> +			more_to_do = 1;
> +		} else if (status < 0) {
> +			no_fifo_space = 1;
> +			break;
> +		}
> +
> +		/* Advance to next QH, skipping start-of-list entry. */
> +		hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next;
> +		if (hcd->non_periodic_qh_ptr ==
&hcd->non_periodic_sched_active)
> +			hcd->non_periodic_qh_ptr =
> +			    hcd->non_periodic_qh_ptr->next;
> +	} while (hcd->non_periodic_qh_ptr != orig_qh_ptr);
> +
> +	if (!hcd->core_if->dma_enable) {
> +		u32 intr_mask = 0;
> +
> +		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +		if (more_to_do || no_queue_space || no_fifo_space) {
> +			/*
> +			 * May need to queue more transactions as the
request
> +			 * queue or Tx FIFO empties. Enable the
non-periodic
> +			 * Tx FIFO empty interrupt. (Always use the
half-empty
> +			 * level to ensure that new requests are loaded as
> +			 * soon as possible.)
> +			 */
> +			dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
> +		} else {
> +			/*
> +			 * Disable the Tx FIFO empty interrupt since there
are
> +			 * no more transactions that need to be queued
right
> +			 * now. This function is called from interrupt
> +			 * handlers to queue more transactions as transfer
> +			 * states change.
> +			 */
> +			dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
> +		}
> +	}
> +}
> +
> +/**
> + * Processes periodic channels for the next frame and queues
transactions for
> + * these channels to the DWC_otg controller. After queueing
transactions, the
> + * Periodic Tx FIFO Empty interrupt is enabled if there are more
transactions
> + * to queue as Periodic Tx FIFO or request queue space becomes
available.
> + * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled.
> + */
> +static void process_periodic_channels(struct dwc_hcd *hcd)
> +{
> +	u32 tx_status = 0;
> +	struct list_head *qh_ptr;
> +	struct dwc_qh *qh;
> +	int status;
> +	int no_queue_space = 0;
> +	int no_fifo_space = 0;
> +	ulong host_regs;

Tabify the declarations ;

> +
> +	host_regs = hcd->core_if->host_if->host_global_regs;
> +
> +	qh_ptr = hcd->periodic_sched_assigned.next;
> +	while (qh_ptr != &hcd->periodic_sched_assigned) {
> +		tx_status = dwc_read32(host_regs + DWC_HPTXSTS);
> +		if (DWC_HPTXSTS_PTXSPC_AVAIL_RD(tx_status) == 0) {
> +			no_queue_space = 1;
> +			break;
> +		}
> +
> +		qh = list_entry(qh_ptr, struct dwc_qh, qh_list_entry);
> +
> +		/*
> +		 * Set a flag if we're queuing high-bandwidth in slave
mode.
> +		 * The flag prevents any halts to get into the request
queue in
> +		 * the middle of multiple high-bandwidth packets getting
queued.
> +		 */
> +		if (!hcd->core_if->dma_enable && qh->channel->multi_count
> 1)
> +			hcd->core_if->queuing_high_bandwidth = 1;
> +
> +		status = queue_transaction(hcd, qh->channel,
> +					   DWC_HPTXSTS_PTXFSPC_AVAIL_RD
> +					   (tx_status));
> +		if (status < 0) {
> +			no_fifo_space = 1;
> +			break;
> +		}
> +
> +		/*
> +		 * In Slave mode, stay on the current transfer until there
is
> +		 * nothing more to do or the high-bandwidth request count
is
> +		 * reached. In DMA mode, only need to queue one request.
The
> +		 * controller automatically handles multiple packets for
> +		 * high-bandwidth transfers.
> +		 */
> +		if (hcd->core_if->dma_enable || (status == 0 ||
> +						 qh->channel->requests ==
> +
qh->channel->multi_count)) {
> +			qh_ptr = qh_ptr->next;
> +
> +			/*
> +			 * Move the QH from the periodic assigned schedule
to
> +			 * the periodic queued schedule.
> +			 */
> +			list_move(&qh->qh_list_entry,
> +				  &hcd->periodic_sched_queued);
> +
> +			/* done queuing high bandwidth */
> +			hcd->core_if->queuing_high_bandwidth = 0;
> +		}
> +	}
> +
> +	if (!hcd->core_if->dma_enable) {
> +		u32 intr_mask = 0;
> +
> +		intr_mask |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +
> +		if (!list_empty(&hcd->periodic_sched_assigned) ||
> +		    no_queue_space || no_fifo_space)
> +			/*
> +			 * May need to queue more transactions as the
request
> +			 * queue or Tx FIFO empties. Enable the periodic
Tx
> +			 * FIFO empty interrupt. (Always use the
half-empty
> +			 * level to ensure that new requests are loaded as
> +			 * soon as possible.)
> +			 */
> +			dwc_modify32(gintmsk_reg(hcd), 0, intr_mask);
> +		else
> +			/*
> +			 * Disable the Tx FIFO empty interrupt since there
are
> +			 * no more transactions that need to be queued
right
> +			 * now. This function is called from interrupt
> +			 * handlers to queue more transactions as transfer
> +			 * states change.
> +			 */
> +			dwc_modify32(gintmsk_reg(hcd), intr_mask, 0);
> +	}
> +}
> +
> +/**
> + * This function processes the currently active host channels and
queues
> + * transactions for these channels to the DWC_otg controller. It is
called
> + * from HCD interrupt handler functions.
> + */
> +void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
> +				    enum dwc_transaction_type tr_type)
> +{
> +	/* Process host channels associated with periodic transfers. */
> +	if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC ||
> +	     tr_type == DWC_OTG_TRANSACTION_ALL) &&
> +	    !list_empty(&hcd->periodic_sched_assigned))
> +		process_periodic_channels(hcd);
> +
> +	/* Process host channels associated with non-periodic transfers.
*/
> +	if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC ||
> +	    tr_type == DWC_OTG_TRANSACTION_ALL) {
> +		if (!list_empty(&hcd->non_periodic_sched_active)) {
> +			process_non_periodic_channels(hcd);
> +		} else {
> +			/*
> +			 * Ensure NP Tx FIFO empty interrupt is disabled
when
> +			 * there are no non-periodic transfers to process.
> +			 */
> +			u32 gintmsk = 0;
> +			gintmsk |= DWC_INTMSK_NP_TXFIFO_EMPT;
> +			dwc_modify32(gintmsk_reg(hcd), gintmsk, 0);
> +		}
> +	}
> +}
> +
> +/**
> + * Sets the final status of an URB and returns it to the device driver.
Any
> + * required cleanup of the URB is performed.
> + */
> +void dwc_otg_hcd_complete_urb(struct dwc_hcd *hcd, struct urb *urb, int
status)
> +__releases(hcd->lock) __acquires(hcd->lock)
> +{
> +	urb->hcpriv = NULL;
> +	usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
> +
> +	spin_unlock(&hcd->lock);
> +	usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
> +	spin_lock(&hcd->lock);
> +}
> diff --git a/drivers/usb/otg/dwc/hcd.h b/drivers/usb/otg/dwc/hcd.h
> new file mode 100644
> index 0000000..c3d86e4
> --- /dev/null
> +++ b/drivers/usb/otg/dwc/hcd.h
> @@ -0,0 +1,416 @@
> +/*
> + * DesignWare HS OTG controller driver
> + * Copyright (C) 2006 Synopsys, Inc.
> + * Portions Copyright (C) 2010 Applied Micro Circuits Corporation.
> + *
> + * This program is free software: you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License version 2 for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see http://www.gnu.org/licenses
> + * or write to the Free Software Foundation, Inc., 51 Franklin Street,
> + * Suite 500, Boston, MA 02110-1335 USA.
> + *
> + * Based on Synopsys driver version 2.60a
> + * Modified by Mark Miesfeld <mmiesfeld at apm.com>
> + * Modified by Stefan Roese <sr at denx.de>, DENX Software Engineering
> + * Modified by Chuck Meade <chuck at theptrgroup.com>
> + *
> + * 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 SYNOPSYS, INC. BE LIABLE FOR ANY
DIRECT,
> + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF
> + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + *
> + */
> +
> +#if !defined(__DWC_HCD_H__)
> +#define __DWC_HCD_H__
> +
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +
> +#include "driver.h"
> +
> +/*
> + * This file contains the structures, constants, and interfaces for
> + * the Host Contoller Driver (HCD).
> + *
> + * The Host Controller Driver (HCD) is responsible for translating
requests
> + * from the USB Driver into the appropriate actions on the DWC_otg
controller.
> + * It isolates the USBD from the specifics of the controller by
providing an
> + * API to the USBD.
> + */
> +
> +/* Phases for control transfers. */
> +enum dwc_control_phase {
> +	DWC_OTG_CONTROL_SETUP,
> +	DWC_OTG_CONTROL_DATA,
> +	DWC_OTG_CONTROL_STATUS
> +};
> +
> +/* Transaction types. */
> +enum dwc_transaction_type {
> +	DWC_OTG_TRANSACTION_NONE,
> +	DWC_OTG_TRANSACTION_PERIODIC,
> +	DWC_OTG_TRANSACTION_NON_PERIODIC,
> +	DWC_OTG_TRANSACTION_ALL
> +};
> +
> +/*
> + * A Queue Transfer Descriptor (QTD) holds the state of a bulk,
control,
> + * interrupt, or isochronous transfer. A single QTD is created for each
URB
> + * (of one of these types) submitted to the HCD. The transfer
associated with
> + * a QTD may require one or multiple transactions.
> + *
> + * A QTD is linked to a Queue Head, which is entered in either the
> + * non-periodic or periodic schedule for execution. When a QTD is
chosen for
> + * execution, some or all of its transactions may be executed. After
> + * execution, the state of the QTD is updated. The QTD may be retired
if all
> + * its transactions are complete or if an error occurred. Otherwise, it
> + * remains in the schedule so more transactions can be executed later.
> + */
> +struct dwc_qtd {
> +	/*
> +	 * Determines the PID of the next data packet for the data phase
of
> +	 * control transfers. Ignored for other transfer types.
> +	 * One of the following values:
> +	 *      - DWC_OTG_HC_PID_DATA0
> +	 *      - DWC_OTG_HC_PID_DATA1
> +	 */
> +	u8 data_toggle;
> +
> +	/* Current phase for control transfers (Setup, Data, or Status).
*/
> +	enum dwc_control_phase control_phase;
> +
> +	/*
> +	 * Keep track of the current split type
> +	 * for FS/LS endpoints on a HS Hub
> +	 */
> +	u8 complete_split;
> +
> +	/* How many bytes transferred during SSPLIT OUT */
> +	u32 ssplit_out_xfer_count;
> +
> +	/*
> +	 * Holds the number of bus errors that have occurred for a
transaction
> +	 * within this transfer.
> +	 */
> +	u8 error_count;
> +
> +	/*
> +	 * Index of the next frame descriptor for an isochronous transfer.
A
> +	 * frame descriptor describes the buffer position and length of
the
> +	 * data to be transferred in the next scheduled (micro)frame of an
> +	 * isochronous transfer. It also holds status for that
transaction.
> +	 * The frame index starts at 0.
> +	 */
> +	int isoc_frame_index;
> +
> +	/* Position of the ISOC split on full/low speed */
> +	u8 isoc_split_pos;
> +
> +	/* Position of the ISOC split in the buffer for the current frame
*/
> +	u16 isoc_split_offset;
> +
> +	/* URB for this transfer */
> +	struct urb *urb;
> +
> +	/* This list of QTDs */
> +	struct list_head qtd_list_entry;
> +
> +	/* Field to track the qh pointer */
> +	struct dwc_qh *qtd_qh_ptr;
> +};
> +
> +/*
> + * A Queue Head (QH) holds the static characteristics of an endpoint
and
> + * maintains a list of transfers (QTDs) for that endpoint. A QH
structure may
> + * be entered in either the non-periodic or periodic schedule.
> + */
> +struct dwc_qh {
> +	/*
> +	 * Endpoint type.
> +	 * One of the following values:
> +	 *      - USB_ENDPOINT_XFER_CONTROL
> +	 *      - USB_ENDPOINT_XFER_ISOC
> +	 *      - USB_ENDPOINT_XFER_BULK
> +	 *      - USB_ENDPOINT_XFER_INT
> +	 */
> +	u8 ep_type;
> +	u8 ep_is_in;
> +
> +	/* wMaxPacketSize Field of Endpoint Descriptor. */
> +	u16 maxp;
> +
> +	/*
> +	 * Determines the PID of the next data packet for non-control
> +	 * transfers. Ignored for control transfers.
> +	 * One of the following values:
> +	 *      - DWC_OTG_HC_PID_DATA0
> +	 *      - DWC_OTG_HC_PID_DATA1
> +	 */
> +	u8 data_toggle;
> +
> +	/* Ping state if 1. */
> +	u8 ping_state;
> +
> +	/* List of QTDs for this QH. */
> +	struct list_head qtd_list;
> +
> +	/* Host channel currently processing transfers for this QH. */
> +	struct dwc_hc *channel;
> +
> +	/* QTD currently assigned to a host channel for this QH. */
> +	struct dwc_qtd *qtd_in_process;
> +
> +	/* Full/low speed endpoint on high-speed hub requires split. */
> +	u8 do_split;
> +
> +	/* Periodic schedule information */
> +
> +	/* Bandwidth in microseconds per (micro)frame. */
> +	u8 usecs;
> +
> +	/* Interval between transfers in (micro)frames. */
> +	u16 interval;
> +
> +	/*
> +	 * (micro)frame to initialize a periodic transfer. The transfer
> +	 * executes in the following (micro)frame.
> +	 */
> +	u16 sched_frame;
> +
> +	/* (micro)frame at which last start split was initialized. */
> +	u16 start_split_frame;
> +
> +	u16 speed;
> +	u16 frame_usecs[8];
> +
> +	/* Entry for QH in either the periodic or non-periodic schedule.
*/
> +	struct list_head qh_list_entry;
> +};
> +
> +/* Gets the struct usb_hcd that contains a struct dwc_hcd. */
> +static inline struct usb_hcd *dwc_otg_hcd_to_hcd(struct dwc_hcd
*dwc_hcd)
> +{
> +	return container_of((void *)dwc_hcd, struct usb_hcd, hcd_priv);
> +}
> +
> +/* HCD Create/Destroy Functions */
> +extern int __init dwc_otg_hcd_init(struct device *_dev,
> +				   struct dwc_otg_device *dwc_dev);
> +extern void dwc_otg_hcd_remove(struct device *_dev);
> +
> +/*
> + * The following functions support managing the DWC_otg controller in
host
> + * mode.
> + */
> +extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd);
> +extern void dwc_otg_hc_cleanup(struct core_if *core_if, struct dwc_hc
*hc);
> +extern void dwc_otg_hc_halt(struct core_if *core_if, struct dwc_hc *hc,
> +			    enum dwc_halt_status _halt_status);
> +
> +/* Transaction Execution Functions */
> +extern enum dwc_transaction_type dwc_otg_hcd_select_transactions(struct
dwc_hcd
> +								 *hcd);
> +extern void dwc_otg_hcd_queue_transactions(struct dwc_hcd *hcd,
> +					   enum dwc_transaction_type
tr_type);
> +extern void dwc_otg_hcd_complete_urb(struct dwc_hcd *_hcd, struct urb
*urb,
> +				     int status);
> +
> +/* Interrupt Handler Functions */
> +extern int dwc_otg_hcd_handle_intr(struct dwc_hcd *hcd);
> +
> +/* Schedule Queue Functions */
> +extern int init_hcd_usecs(struct dwc_hcd *hcd);
> +extern void dwc_otg_hcd_qh_free(struct dwc_qh *qh);
> +extern void dwc_otg_hcd_qh_remove(struct dwc_hcd *hcd, struct dwc_qh
*qh);
> +extern void dwc_otg_hcd_qh_deactivate(struct dwc_hcd *hcd, struct
dwc_qh *qh,
> +				      int sched_csplit);
> +extern int dwc_otg_hcd_qh_deferr(struct dwc_hcd *hcd, struct dwc_qh
*qh,
> +				 int delay);
> +extern struct dwc_qtd *dwc_otg_hcd_qtd_create(struct urb *urb,
> +					      gfp_t _mem_flags);
> +extern int dwc_otg_hcd_qtd_add(struct dwc_qtd *qtd, struct dwc_hcd
*dwc_hcd);
> +
> +/*
> + * Frees the memory for a QTD structure.  QTD should already be removed
from
> + * list.
> + */
> +static inline void dwc_otg_hcd_qtd_free(struct dwc_qtd *_qtd)
> +{
> +	kfree(_qtd);
> +}
> +
> +/* Removes a QTD from list. */
> +static inline void dwc_otg_hcd_qtd_remove(struct dwc_qtd *_qtd)
> +{
> +	list_del(&_qtd->qtd_list_entry);
> +}
> +
> +/* Remove and free a QTD */
> +static inline void dwc_otg_hcd_qtd_remove_and_free(struct dwc_qtd
*_qtd)
> +{
> +	dwc_otg_hcd_qtd_remove(_qtd);
> +	dwc_otg_hcd_qtd_free(_qtd);
> +}
> +
> +struct dwc_qh *dwc_urb_to_qh(struct urb *_urb);
> +
> +/* Gets the usb_host_endpoint associated with an URB. */
> +static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb
*_urb)
> +{
> +	struct usb_device *dev = _urb->dev;
> +	int ep_num = usb_pipeendpoint(_urb->pipe);
> +
> +	if (usb_pipein(_urb->pipe))
> +		return dev->ep_in[ep_num];
> +	else
> +		return dev->ep_out[ep_num];
> +}
> +
> +/*
> + * Gets the endpoint number from a _bEndpointAddress argument. The
endpoint is
> + * qualified with its direction (possible 32 endpoints per device).
> + */
> +#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) \
> +		((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \
> +		((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4)
> +
> +/* Gets the QH that contains the list_head */
> +#define dwc_list_to_qh(_list_head_ptr_) \
> +		(container_of(_list_head_ptr_, struct dwc_qh,
qh_list_entry))
> +
> +/* Gets the QTD that contains the list_head */
> +#define dwc_list_to_qtd(_list_head_ptr_) \
> +		(container_of(_list_head_ptr_, struct dwc_qtd,
qtd_list_entry))
> +
> +/* Check if QH is non-periodic  */
> +#define dwc_qh_is_non_per(_qh_ptr_) \
> +		((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \
> +		(_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL))
> +
> +/* High bandwidth multiplier as encoded in highspeed endpoint
descriptors */
> +#define dwc_hb_mult(wMaxPacketSize)	(1 + (((wMaxPacketSize) >> 11) &
0x03))
> +
> +/* Packet size for any kind of endpoint descriptor */
> +#define dwc_max_packet(wMaxPacketSize)	((wMaxPacketSize) &
0x07ff)
> +
> +/*
> + * Returns true if _frame1 is less than or equal to _frame2. The
comparison is
> + * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of
the
> + * frame number when the max frame number is reached.
> + */
> +static inline int dwc_frame_num_le(u16 _frame1, u16 _frame2)
> +{
> +	return ((_frame2 - _frame1) & DWC_HFNUM_MAX_FRNUM) <=
> +	    (DWC_HFNUM_MAX_FRNUM >> 1);
> +}
> +
> +/*
> + * Returns true if _frame1 is greater than _frame2. The comparison is
done
> + * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the
frame
> + * number when the max frame number is reached.
> + */
> +static inline int dwc_frame_num_gt(u16 _frame1, u16 _frame2)
> +{
> +	return (_frame1 != _frame2) &&
> +	    (((_frame1 - _frame2) &
> +	      DWC_HFNUM_MAX_FRNUM) < (DWC_HFNUM_MAX_FRNUM >> 1));
> +}
> +
> +/*
> + * Increments _frame by the amount specified by _inc. The addition is
done
> + * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value.
> + */
> +static inline u16 dwc_frame_num_inc(u16 _frame, u16 _inc)
> +{
> +	return (_frame + _inc) & DWC_HFNUM_MAX_FRNUM;
> +}
> +
> +static inline u16 dwc_full_frame_num(u16 _frame)
> +{
> +	return ((_frame) & DWC_HFNUM_MAX_FRNUM) >> 3;
> +}
> +
> +static inline u16 dwc_micro_frame_num(u16 _frame)
> +{
> +	return (_frame) & 0x7;
> +}
> +
> +static inline ulong gintsts_reg(struct dwc_hcd *hcd)
> +{
> +	ulong global_regs = hcd->core_if->core_global_regs;
> +	return global_regs + DWC_GINTSTS;
> +}
> +
> +static inline ulong gintmsk_reg(struct dwc_hcd *hcd)
> +{
> +	ulong global_regs = hcd->core_if->core_global_regs;
> +	return global_regs + DWC_GINTMSK;
> +}
> +
> +static inline ulong gahbcfg_reg(struct dwc_hcd *hcd)
> +{
> +	ulong global_regs = hcd->core_if->core_global_regs;
> +	return global_regs + DWC_GAHBCFG;
> +}
> +
> +static inline const char *pipetype_str(unsigned int pipe)
> +{
> +	switch (usb_pipetype(pipe)) {
> +	case PIPE_CONTROL:
> +		return "control";
> +	case PIPE_BULK:
> +		return "bulk";
> +	case PIPE_INTERRUPT:
> +		return "interrupt";
> +	case PIPE_ISOCHRONOUS:
> +		return "isochronous";
> +	default:
> +		return "unknown";
> +	}
> +}
> +
> +static inline const char *dev_speed_str(enum usb_device_speed speed)
> +{
> +	switch (speed) {
> +	case USB_SPEED_HIGH:
> +		return "high";
> +	case USB_SPEED_FULL:
> +		return "full";
> +	case USB_SPEED_LOW:
> +		return "low";
> +	default:
> +		return "unknown";
> +	}
> +}
> +
> +static inline const char *ep_type_str(u8 type)
> +{
> +	switch (type) {
> +	case USB_ENDPOINT_XFER_ISOC:
> +		return "isochronous";
> +	case USB_ENDPOINT_XFER_INT:
> +		return "interrupt";
> +	case USB_ENDPOINT_XFER_CONTROL:
> +		return "control";
> +	case USB_ENDPOINT_XFER_BULK:
> +		return "bulk";
> +	default:
> +		return "?";
> +	}
> +}
> +#endif
> --
> 1.6.1.rc3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


More information about the Linuxppc-dev mailing list