Archived
1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
archive/package/kernel/lantiq/ltq-hcd/src/ifxhcd.c
John Crispin cd48f51760 lantiq: fix use of IRQF_DISABLED in lantiq kmods
The IRQF_DISABLED flag was removed in kernel 4.1 with commit
"genirq: Remove the deprecated 'IRQF_DISABLED' request_irq() flag entirely" [1].

Therefore the compilation of ltq-hcd and ltq-vmmc kmods fails.

[1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=d8bf368d0631d4bc2612d8bf2e4e8e74e620d0cc

Signed-off-by: Mathias Kresin <openwrt@kresin.me>

SVN-Revision: 47180
2015-10-11 16:24:43 +00:00

2139 lines
64 KiB
C

/*****************************************************************************
** FILE NAME : ifxhcd.c
** PROJECT : IFX USB sub-system V3
** MODULES : IFX USB sub-system Host and Device driver
** SRC VERSION : 3.2
** DATE : 1/Jan/2011
** AUTHOR : Chen, Howard
** DESCRIPTION : 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 IFXUSB controller.
** It isolates the USBD from the specifics of the
** controller by providing an API to the USBD.
** FUNCTIONS :
** COMPILER : gcc
** REFERENCE : Synopsys DWC-OTG Driver 2.7
** COPYRIGHT : Copyright (c) 2010
** LANTIQ DEUTSCHLAND GMBH,
** Am Campeon 3, 85579 Neubiberg, Germany
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** Version Control Section **
** $Author$
** $Date$
** $Revisions$
** $Log$ Revision history
*****************************************************************************/
/*
* This file contains code fragments from Synopsys HS OTG Linux Software Driver.
* For this code the following notice is applicable:
*
* ==========================================================================
*
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
* otherwise expressly agreed to in writing between Synopsys and you.
*
* The Software IS NOT an item of Licensed Software or Licensed Product under
* any End User Software License Agreement or Agreement for Licensed Product
* with Synopsys or any supplement thereto. You are permitted to use and
* redistribute this Software in source and binary forms, with or without
* modification, provided that redistributions of source code must retain this
* notice. You may not view, use, disclose, copy or distribute this file or
* any information contained herein except pursuant to this license grant from
* Synopsys. If you do not agree with this notice, including the disclaimer
* below, then you are not authorized to use the Software.
*
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
* ========================================================================== */
/*!
\file ifxhcd.c
\ingroup IFXUSB_DRIVER_V3
\brief This file contains the implementation of the HCD. In Linux,
the HCD implements the hc_driver API.
*/
#include <linux/version.h>
#include "ifxusb_version.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/dma-mapping.h>
#include "ifxusb_plat.h"
#include "ifxusb_regs.h"
#include "ifxusb_cif.h"
#include "ifxhcd.h"
#include <asm/irq.h>
#ifdef __DEBUG__
static void dump_urb_info(struct urb *_urb, char* _fn_name);
#if 0
static void dump_channel_info(ifxhcd_hcd_t *_ifxhcd,
ifxhcd_epqh_t *_epqh);
#endif
#endif
static void ifxhcd_complete_urb_sub(ifxhcd_urbd_t *_urbd)
{
ifxhcd_hcd_t *ifxhcd;
struct urb *urb=NULL;
if (!list_empty(&_urbd->ql))
{
list_del_init(&_urbd->ql);
_urbd->epqh->urbd_count--;
}
else
IFX_ERROR("%s: urb(%p) not connect to any epqh\n",
__func__,_urbd);
ifxhcd=_urbd->epqh->ifxhcd;
urb =_urbd->urb;
if(!urb)
IFX_ERROR("%s: invalid urb\n",__func__);
else if(urb->hcpriv)
{
if(urb->hcpriv != _urbd)
IFX_ERROR("%s: invalid"
" urb(%p)->hcpriv(%p) != _urbd(%p)\n",
__func__,
urb,
urb->hcpriv,
_urbd);
#if defined(__UNALIGNED_BUF_ADJ__)
if(_urbd->is_in &&
// _urbd->using_aligned_buf &&
_urbd->aligned_buf)
memcpy(_urbd->xfer_buff,
_urbd->aligned_buf,
_urbd->xfer_len);
if(_urbd->aligned_buf)
ifxusb_free_buf_h(_urbd->aligned_buf);
#endif
urb->hcpriv = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
urb->status=_urbd->status;
usb_hcd_giveback_urb(ifxhcd_to_syshcd(ifxhcd), urb);
#else
usb_hcd_giveback_urb(ifxhcd_to_syshcd(ifxhcd), urb,
_urbd->status);
#endif
}
kfree(_urbd);
}
#ifdef __STRICT_ORDER__
static void ifxhcd_complete_urb_func(unsigned long data)
{
unsigned long flags;
ifxhcd_urbd_t *urbd;
ifxhcd_epqh_t *epqh;
struct list_head *item;
int count=10;
epqh=((ifxhcd_epqh_t *)data);
while (!list_empty(&epqh->release_list) && count)
{
item = epqh->release_list.next;
urbd = list_entry(item, ifxhcd_urbd_t, ql);
if (!urbd)
IFX_ERROR("%s: invalid urbd\n",__func__);
else if (!urbd->epqh)
IFX_ERROR("%s: invalid epqd\n",__func__);
else
{
ifxhcd_epqh_t *epqh2;
epqh2=urbd->epqh;
local_irq_save(flags);
LOCK_URBD_LIST(epqh2);
ifxhcd_complete_urb_sub(urbd);
UNLOCK_URBD_LIST(epqh2);
local_irq_restore (flags);
}
count--;
}
if(!list_empty(&epqh->release_list))
tasklet_schedule(&epqh->complete_urb_sub);
}
/*!
\brief Sets the final status of an URB and returns it to the device
driver. Any required cleanup of the URB is performed.
*/
void ifxhcd_complete_urb(ifxhcd_hcd_t *_ifxhcd,
ifxhcd_urbd_t *_urbd,
int _status)
{
unsigned long flags;
if(!_urbd)
{
IFX_ERROR("%s: invalid urbd\n",__func__);
return;
}
if (!_urbd->epqh)
{
IFX_ERROR("%s: invalid epqh\n",__func__);
return;
}
local_irq_save(flags);
LOCK_URBD_LIST(_urbd->epqh);
#ifdef __DEBUG__
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB))
{
IFX_PRINT("%s: ehqh %p _urbd %p, urb %p,"
" device %d, ep %d %s/%s, status=%d\n",
__func__,_urbd->epqh,
_urbd,_urbd->urb,
(_urbd->urb)?usb_pipedevice(_urbd->urb->pipe):-1,
(_urbd->urb)?usb_pipeendpoint(_urbd->urb->pipe):-1,
(_urbd->urb)?(usb_pipein(_urbd->urb->pipe) ? "IN" : "OUT"):"--",
(_urbd->is_in) ? "IN" : "OUT",
_status);
if ((_urbd->urb)&& _urbd->epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
{
int i;
for (i = 0; i < _urbd->urb->number_of_packets; i++)
IFX_PRINT(" ISO Desc %d status: %d\n", i, _urbd->urb->iso_frame_desc[i].status);
}
}
#endif
_urbd->status = _status;
if(_urbd->phase!=URBD_FINISHING)
{
if(_urbd->phase!=URBD_DEQUEUEING && _urbd->phase!=URBD_COMPLETING)
printk(KERN_INFO "Warning: %s() Strange URBD PHASE %d\n",__func__,_urbd->phase);
if(_urbd->urb)
{
if( _urbd->status == 0
&& _urbd->phase==URBD_COMPLETING
&& in_irq())
{
list_move_tail(&_urbd->ql,&_urbd->epqh->release_list);
if(!_urbd->epqh->complete_urb_sub.func)
{
_urbd->epqh->complete_urb_sub.next = NULL;
_urbd->epqh->complete_urb_sub.state = 0;
atomic_set( &_urbd->epqh->complete_urb_sub.count, 0);
_urbd->epqh->complete_urb_sub.func = ifxhcd_complete_urb_func;
_urbd->epqh->complete_urb_sub.data = (unsigned long)_urbd->epqh;
}
tasklet_schedule(&_urbd->epqh->complete_urb_sub);
}
else
{
_urbd->phase=URBD_FINISHING;
ifxhcd_complete_urb_sub(_urbd);
}
UNLOCK_URBD_LIST(_urbd->epqh);
}
else
{
UNLOCK_URBD_LIST(_urbd->epqh);
kfree(_urbd);
}
}
else
{
printk(KERN_INFO "Warning: %s() Double Completing \n",__func__);
UNLOCK_URBD_LIST(_urbd->epqh);
}
local_irq_restore (flags);
}
#else
static void ifxhcd_complete_urb_func(unsigned long data)
{
unsigned long flags;
ifxhcd_urbd_t *urbd;
urbd=((ifxhcd_urbd_t *)data);
// local_irq_save(flags);
if (!urbd)
IFX_ERROR("%s: invalid urbd\n",__func__);
else if (!urbd->epqh)
IFX_ERROR("%s: invalid epqd\n",__func__);
else
{
local_irq_save(flags);
LOCK_URBD_LIST(urbd->epqh);
ifxhcd_complete_urb_sub(urbd);
UNLOCK_URBD_LIST(urbd->epqh);
local_irq_restore (flags);
}
// local_irq_restore (flags);
}
/*!
\brief Sets the final status of an URB and returns it to the device driver. Any
required cleanup of the URB is performed.
*/
void ifxhcd_complete_urb(ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status)
{
unsigned long flags;
if(!_urbd)
{
IFX_ERROR("%s: invalid urbd\n",__func__);
return;
}
if (!_urbd->epqh)
{
IFX_ERROR("%s: invalid epqh\n",__func__);
return;
}
local_irq_save(flags);
LOCK_URBD_LIST(_urbd->epqh);
#ifdef __DEBUG__
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB))
{
IFX_PRINT("%s: ehqh %p _urbd %p, urb %p, device %d, ep %d %s/%s, status=%d\n",
__func__,_urbd->epqh, _urbd,_urbd->urb,
(_urbd->urb)?usb_pipedevice(_urbd->urb->pipe):-1,
(_urbd->urb)?usb_pipeendpoint(_urbd->urb->pipe):-1,
(_urbd->urb)?(usb_pipein(_urbd->urb->pipe) ? "IN" : "OUT"):"--",
(_urbd->is_in) ? "IN" : "OUT",
_status);
if ((_urbd->urb)&& _urbd->epqh->ep_type == IFXUSB_EP_TYPE_ISOC)
{
int i;
for (i = 0; i < _urbd->urb->number_of_packets; i++)
IFX_PRINT(" ISO Desc %d status: %d\n", i, _urbd->urb->iso_frame_desc[i].status);
}
}
#endif
_urbd->status = _status;
if(_urbd->phase!=URBD_FINISHING)
{
if(_urbd->phase!=URBD_DEQUEUEING && _urbd->phase!=URBD_COMPLETING)
printk(KERN_INFO "Warning: %s() Strange URBD PHASE %d\n",__func__,_urbd->phase);
if(_urbd->urb)
{
if( _urbd->status == 0
&& _urbd->phase==URBD_COMPLETING
&& in_irq())
{
if(_urbd->complete_urb_sub.func)
printk(KERN_INFO "Warning: %s() URBD Tasklet is on already\n",__func__);
_urbd->phase=URBD_FINISHING;
_urbd->complete_urb_sub.next = NULL;
_urbd->complete_urb_sub.state = 0;
atomic_set( &_urbd->complete_urb_sub.count, 0);
_urbd->complete_urb_sub.func = ifxhcd_complete_urb_func;
_urbd->complete_urb_sub.data = (unsigned long)_urbd;
tasklet_schedule(&_urbd->complete_urb_sub);
}
else
{
_urbd->phase=URBD_FINISHING;
ifxhcd_complete_urb_sub(_urbd);
}
}
else
kfree(_urbd);
}
else
printk(KERN_INFO "Warning: %s() Double Completing \n",__func__);
UNLOCK_URBD_LIST(_urbd->epqh);
local_irq_restore (flags);
}
#endif
/*!
\brief Processes all the URBs in a single EPQHs. Completes them with
status and frees the URBD.
*/
static
void kill_all_urbs_in_epqh(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh, int _status)
{
struct list_head *item;
struct list_head *next;
ifxhcd_urbd_t *urbd;
if(!_epqh)
return;
IFX_DEBUGPL(DBG_HCDV, "%s %p\n",__func__,_epqh);
LOCK_URBD_LIST(_epqh);
list_for_each(item, &_epqh->urbd_list)
{
urbd = list_entry(item, ifxhcd_urbd_t, ql);
if( urbd->phase==URBD_IDLE
|| urbd->phase==URBD_ACTIVE
// || urbd->phase==URBD_STARTING
)
urbd->phase=URBD_DEQUEUEING;
}
list_for_each_safe(item, next, &_epqh->urbd_list)
{
urbd = list_entry(item, ifxhcd_urbd_t, ql);
if(urbd->phase==URBD_DEQUEUEING)
{
urbd->urb->status = _status;
urbd->phase = URBD_FINISHING;
ifxhcd_complete_urb_sub(urbd);
}
else if( urbd->phase==URBD_STARTED
|| urbd->phase==URBD_STARTING
// || urbd->phase==URBD_ACTIVE
)
{
if(ifxhcd_hc_halt(&_ifxhcd->core_if, _epqh->hc, HC_XFER_URB_DEQUEUE))
{
urbd->urb->status = _status;
urbd->phase=URBD_FINISHING;
ifxhcd_complete_urb_sub(urbd);
}
}
else
IFX_ERROR("%s: invalid urb phase:%d \n",__func__,urbd->phase);
}
UNLOCK_URBD_LIST(_epqh);
IFX_DEBUGPL(DBG_HCDV, "%s %p finish\n",__func__,_epqh);
}
/*!
\brief Free all EPS in one Processes all the URBs in a single list of EPQHs. Completes them with
-ETIMEDOUT and frees the URBD.
*/
static
void epqh_list_free_1(ifxhcd_hcd_t *_ifxhcd, struct list_head *_epqh_list)
{
ifxhcd_epqh_t *epqh;
struct list_head *item;
if (!_ifxhcd)
return;
if (!_epqh_list)
return;
IFX_DEBUGPL(DBG_HCDV, "%s %p\n",__func__,_epqh_list);
item = _epqh_list->next;
while(item != _epqh_list && item != item->next)
{
epqh = list_entry(item, ifxhcd_epqh_t, ql);
epqh->phase=EPQH_DISABLING;
item = item->next;
kill_all_urbs_in_epqh(_ifxhcd, epqh, -ETIMEDOUT);
#ifdef __STRICT_ORDER__
if(list_empty(&epqh->urbd_list) && list_empty(&epqh->release_list))
#else
if(list_empty(&epqh->urbd_list))
#endif
ifxhcd_epqh_free(epqh);
}
IFX_DEBUGPL(DBG_HCDV, "%s %p finish\n",__func__,_epqh_list);
/* Ensure there are no URBDs or URBs left. */
}
static
void epqh_list_free_2(ifxhcd_hcd_t *_ifxhcd, struct list_head *_epqh_list)
{
ifxhcd_epqh_t *epqh;
struct list_head *item;
struct list_head *next;
if (!_ifxhcd)
return;
if (!_epqh_list)
return;
IFX_DEBUGPL(DBG_HCDV, "%s %p\n",__func__,_epqh_list);
list_for_each_safe(item, next, _epqh_list)
{
epqh = list_entry(item, ifxhcd_epqh_t, ql);
if(item == item->next)
{
ifxhcd_epqh_free(epqh);
}
else
{
uint32_t count=0x80000;
#ifdef __STRICT_ORDER__
for(;(!list_empty(&epqh->urbd_list) || !list_empty(&epqh->release_list))&& count> 0; count--) udelay(1);
#else
for(;!list_empty(&epqh->urbd_list) && count> 0; count--) udelay(1);
#endif
if(!count)
IFX_ERROR("%s: unable to clear urbd in epqh \n",__func__);
ifxhcd_epqh_free(epqh);
}
}
IFX_DEBUGPL(DBG_HCDV, "%s %p finish\n",__func__,_epqh_list);
/* Ensure there are no URBDs or URBs left. */
}
static
void epqh_list_free_all_sub(unsigned long data)
{
ifxhcd_hcd_t *ifxhcd;
ifxhcd=(ifxhcd_hcd_t *)data;
epqh_list_free_1(ifxhcd, &ifxhcd->epqh_list_np );
epqh_list_free_1(ifxhcd, &ifxhcd->epqh_list_intr);
#ifdef __EN_ISOC__
epqh_list_free_1(ifxhcd, &ifxhcd->epqh_list_isoc);
#endif
epqh_list_free_2(ifxhcd, &ifxhcd->epqh_list_np );
epqh_list_free_2(ifxhcd, &ifxhcd->epqh_list_intr);
#ifdef __EN_ISOC__
epqh_list_free_2(ifxhcd, &ifxhcd->epqh_list_isoc);
#endif
}
static
void epqh_list_free_all(ifxhcd_hcd_t *_ifxhcd)
{
_ifxhcd->tasklet_free_epqh_list.next = NULL;
_ifxhcd->tasklet_free_epqh_list.state = 0;
atomic_set( &_ifxhcd->tasklet_free_epqh_list.count, 0);
_ifxhcd->tasklet_free_epqh_list.func=epqh_list_free_all_sub;
_ifxhcd->tasklet_free_epqh_list.data = (unsigned long)_ifxhcd;
tasklet_schedule(&_ifxhcd->tasklet_free_epqh_list);
}
/*!
\brief This function is called to handle the disconnection of host port.
*/
int32_t ifxhcd_disconnect(ifxhcd_hcd_t *_ifxhcd)
{
IFX_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _ifxhcd);
_ifxhcd->disconnecting=1;
/* Set status flags for the hub driver. */
_ifxhcd->flags.b.port_connect_status_change = 1;
_ifxhcd->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.
*/
{
gint_data_t intr = { .d32 = 0 };
intr.b.nptxfempty = 1;
intr.b.ptxfempty = 1;
intr.b.hcintr = 1;
ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gintmsk, intr.d32, 0);
ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gintsts, intr.d32, 0);
}
/* Respond with an error status to all URBs in the schedule. */
epqh_list_free_all(_ifxhcd);
/* Clean up any host channels that were in use. */
{
int num_channels;
ifxhcd_hc_t *channel;
ifxusb_hc_regs_t *hc_regs;
hcchar_data_t hcchar;
int i;
num_channels = _ifxhcd->core_if.params.host_channels;
for (i = 0; i < num_channels; i++)
{
channel = &_ifxhcd->ifxhc[i];
hc_regs = _ifxhcd->core_if.hc_regs[i];
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
if (hcchar.b.chen)
printk(KERN_INFO "Warning: %s() HC still enabled\n",__func__);
ifxhcd_hc_cleanup(&_ifxhcd->core_if, channel);
}
}
IFX_DEBUGPL(DBG_HCDV, "%s(%p) finish\n", __func__, _ifxhcd);
return 1;
}
/*!
\brief Frees secondary storage associated with the ifxhcd_hcd structure contained
in the struct usb_hcd field.
*/
static void ifxhcd_freeextra(struct usb_hcd *_syshcd)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd);
IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD FREE\n");
/* Free memory for EPQH/URBD lists */
epqh_list_free_all(ifxhcd);
/* Free memory for the host channels. */
ifxusb_free_buf_h(ifxhcd->status_buf);
return;
}
/*!
\brief Initializes the HCD. This function allocates memory for and initializes the
static parts of the usb_hcd and ifxhcd_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 ifxhcd_init(ifxhcd_hcd_t *_ifxhcd)
{
int retval = 0;
struct usb_hcd *syshcd = NULL;
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD INIT\n");
INIT_EPQH_LIST_ALL(_ifxhcd);
INIT_EPQH_LIST(_ifxhcd);
init_timer(&_ifxhcd->autoprobe_timer);
init_timer(&_ifxhcd->host_probe_timer);
_ifxhcd->probe_sec = 5;
_ifxhcd->autoprobe_sec = 30;
_ifxhcd->hc_driver.description = _ifxhcd->core_if.core_name;
_ifxhcd->hc_driver.product_desc = "IFX USB Controller";
//_ifxhcd->hc_driver.hcd_priv_size = sizeof(ifxhcd_hcd_t);
_ifxhcd->hc_driver.hcd_priv_size = sizeof(unsigned long);
_ifxhcd->hc_driver.irq = ifxhcd_irq;
_ifxhcd->hc_driver.flags = HCD_MEMORY | HCD_USB2;
_ifxhcd->hc_driver.start = ifxhcd_start;
_ifxhcd->hc_driver.stop = ifxhcd_stop;
//_ifxhcd->hc_driver.reset =
//_ifxhcd->hc_driver.suspend =
//_ifxhcd->hc_driver.resume =
_ifxhcd->hc_driver.urb_enqueue = ifxhcd_urb_enqueue;
_ifxhcd->hc_driver.urb_dequeue = ifxhcd_urb_dequeue;
_ifxhcd->hc_driver.endpoint_disable = ifxhcd_endpoint_disable;
_ifxhcd->hc_driver.get_frame_number = ifxhcd_get_frame_number;
_ifxhcd->hc_driver.hub_status_data = ifxhcd_hub_status_data;
_ifxhcd->hc_driver.hub_control = ifxhcd_hub_control;
//_ifxhcd->hc_driver.hub_suspend =
//_ifxhcd->hc_driver.hub_resume =
_ifxhcd->pkt_remaining_reload_hs=PKT_REMAINING_RELOAD_HS;
_ifxhcd->pkt_remaining_reload_fs=PKT_REMAINING_RELOAD_FS;
_ifxhcd->pkt_remaining_reload_ls=PKT_REMAINING_RELOAD_LS;
_ifxhcd->pkt_count_limit_bo =8;
_ifxhcd->pkt_count_limit_bi =8;
/* Allocate memory for and initialize the base HCD and */
//syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->dev->bus_id);
syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->core_if.core_name);
if (syshcd == NULL)
{
retval = -ENOMEM;
goto error1;
}
syshcd->rsrc_start = (unsigned long)_ifxhcd->core_if.core_global_regs;
syshcd->regs = (void *)_ifxhcd->core_if.core_global_regs;
syshcd->self.otg_port = 0;
//*((unsigned long *)(&(syshcd->hcd_priv)))=(unsigned long)_ifxhcd;
//*((unsigned long *)(&(syshcd->hcd_priv[0])))=(unsigned long)_ifxhcd;
syshcd->hcd_priv[0]=(unsigned long)_ifxhcd;
_ifxhcd->syshcd=syshcd;
INIT_LIST_HEAD(&_ifxhcd->epqh_list_all );
INIT_LIST_HEAD(&_ifxhcd->epqh_list_np );
INIT_LIST_HEAD(&_ifxhcd->epqh_list_intr );
#ifdef __EN_ISOC__
INIT_LIST_HEAD(&_ifxhcd->epqh_list_isoc);
#endif
/*
* Create a host channel descriptor for each host channel implemented
* in the controller. Initialize the channel descriptor array.
*/
{
int num_channels = _ifxhcd->core_if.params.host_channels;
int i;
for (i = 0; i < num_channels; i++)
{
_ifxhcd->ifxhc[i].hc_num = i;
IFX_DEBUGPL(DBG_HCDV, "HCD Added channel #%d\n", i);
}
}
/* Set device flags indicating whether the HCD supports DMA. */
if(_ifxhcd->dev->dma_mask)
*(_ifxhcd->dev->dma_mask) = ~0;
_ifxhcd->dev->coherent_dma_mask = ~0;
/*
* 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 ifxusb_hcd_start method.
*/
retval = usb_add_hcd(syshcd, _ifxhcd->core_if.irq, 0 | IRQF_SHARED);
if (retval < 0)
goto error2;
/*
* 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.
*/
_ifxhcd->status_buf = ifxusb_alloc_buf_h(IFXHCD_STATUS_BUF_SIZE, 1);
if (_ifxhcd->status_buf)
{
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Initialized, bus=%s, usbbus=%d\n", _ifxhcd->core_if.core_name, syshcd->self.busnum);
return 0;
}
IFX_ERROR("%s: status_buf allocation failed\n", __func__);
/* Error conditions */
usb_remove_hcd(syshcd);
error2:
ifxhcd_freeextra(syshcd);
usb_put_hcd(syshcd);
error1:
return retval;
}
/*!
\brief Removes the HCD.
Frees memory and resources associated with the HCD and deregisters the bus.
*/
void ifxhcd_remove(ifxhcd_hcd_t *_ifxhcd)
{
struct usb_hcd *syshcd = ifxhcd_to_syshcd(_ifxhcd);
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD REMOVE\n");
/* Turn off all interrupts */
ifxusb_wreg (&_ifxhcd->core_if.core_global_regs->gintmsk, 0);
ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gahbcfg, 1, 0);
usb_remove_hcd(syshcd);
ifxhcd_freeextra(syshcd);
usb_put_hcd(syshcd);
return;
}
/* =========================================================================
* Linux HC Driver Functions
* ========================================================================= */
/*!
\brief Initializes the IFXUSB 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.
Called by USB stack.
*/
int ifxhcd_start(struct usb_hcd *_syshcd)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd);
ifxusb_core_if_t *core_if = &ifxhcd->core_if;
struct usb_bus *bus;
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD START\n");
bus = hcd_to_bus(_syshcd);
/* Initialize the bus state. */
_syshcd->state = HC_STATE_RUNNING;
/* Initialize and connect root hub if one is not already attached */
if (bus->root_hub)
{
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Has Root Hub\n");
/* Inform the HUB driver to resume. */
usb_hcd_resume_root_hub(_syshcd);
}
ifxhcd->flags.d32 = 0;
/* Put all channels in the free channel list and clean up channel states.*/
{
int num_channels = ifxhcd->core_if.params.host_channels;
int i;
for (i = 0; i < num_channels; i++)
{
ifxhcd_hc_t *channel;
channel = &ifxhcd->ifxhc[i];
ifxhcd_hc_cleanup(&ifxhcd->core_if, channel);
}
}
/* Initialize the USB core for host mode operation. */
ifxusb_host_enable_interrupts(core_if);
ifxusb_enable_global_interrupts_h(core_if);
ifxusb_phy_power_on_h (core_if);
ifxusb_vbus_init(core_if);
/* Turn on the vbus power. */
{
hprt0_data_t hprt0;
hprt0.d32 = ifxusb_read_hprt0(core_if);
IFX_PRINT("Init: Power Port (%d)\n", hprt0.b.prtpwr);
if (hprt0.b.prtpwr == 0 )
{
hprt0.b.prtpwr = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
ifxusb_vbus_on(core_if);
}
}
return 0;
}
/*!
\brief Halts the IFXUSB host mode operations in a clean manner. USB transfers are
stopped.
*/
#if defined(__IS_AR10__)
void ifxusb_oc_int_free(int port);
#else
void ifxusb_oc_int_free(void);
#endif
void ifxhcd_stop(struct usb_hcd *_syshcd)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd);
hprt0_data_t hprt0 = { .d32=0 };
IFX_DEBUGPL(DBG_HCD, "IFX USB HCD STOP\n");
/* Turn off all interrupts. */
ifxusb_disable_global_interrupts_h(&ifxhcd->core_if );
ifxusb_host_disable_interrupts(&ifxhcd->core_if );
/*
* The root hub should be disconnected before this function is called.
* The disconnect will clear the URBD lists (via ..._hcd_urb_dequeue)
* and the EPQH lists (via ..._hcd_endpoint_disable).
*/
/* Turn off the vbus power */
IFX_PRINT("PortPower off\n");
ifxusb_vbus_off(&ifxhcd->core_if );
#if defined(__IS_AR10__)
ifxusb_oc_int_free(ifxhcd->core_if.core_no);
#else
ifxusb_oc_int_free();
#endif
ifxusb_vbus_free(&ifxhcd->core_if );
hprt0.b.prtpwr = 0;
ifxusb_wreg(ifxhcd->core_if.hprt0, hprt0.d32);
return;
}
/*!
\brief Returns the current frame number
*/
int ifxhcd_get_frame_number(struct usb_hcd *_syshcd)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd);
hfnum_data_t hfnum;
hfnum.d32 = ifxusb_rreg(&ifxhcd->core_if.host_global_regs->hfnum);
return hfnum.b.frnum;
}
/*!
\brief 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.
*/
int ifxhcd_urb_enqueue( struct usb_hcd *_syshcd,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
struct usb_host_endpoint *_sysep,
#endif
struct urb *_urb,
gfp_t _mem_flags)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd);
ifxhcd_epqh_t *epqh = NULL;
#ifdef __DEBUG__
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB))
dump_urb_info(_urb, "ifxusb_hcd_urb_enqueue");
#endif //__DEBUG__
if (!ifxhcd->flags.b.port_connect_status) /* No longer connected. */
return -ENODEV;
#if !defined(__EN_ISOC__)
if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS)
{
IFX_ERROR("ISOC transfer not supported!!!\n");
return -ENODEV;
}
#endif
if(_urb->hcpriv)
{
IFX_WARN("%s() Previous urb->hcpriv exist %p\n",__func__,_urb->hcpriv);
#if 1
return -ENOSPC;
#endif
}
epqh=ifxhcd_urbd_create (ifxhcd,_urb);
if (!epqh)
{
IFX_ERROR("IFXUSB HCD URB Enqueue failed creating URBD\n");
return -ENOSPC;
}
if(epqh->phase==EPQH_DISABLING )
{
IFX_ERROR("Enqueue to a DISABLING EP!!!\n");
return -ENODEV;
}
#ifdef __DYN_SOF_INTR__
ifxhcd->dyn_sof_count = DYN_SOF_COUNT_DEF;
#endif
//enable_sof(ifxhcd);
{
gint_data_t gintsts;
gintsts.d32=0;
gintsts.b.sofintr = 1;
ifxusb_mreg(&ifxhcd->core_if.core_global_regs->gintmsk, 0,gintsts.d32);
}
if(epqh->phase==EPQH_IDLE || epqh->phase==EPQH_STDBY )
{
epqh->phase=EPQH_READY;
#ifdef __EPQD_DESTROY_TIMEOUT__
del_timer(&epqh->destroy_timer);
#endif
}
select_eps(ifxhcd);
return 0;
}
/*!
\brief Aborts/cancels a USB transfer request. Always returns 0 to indicate
success.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
int ifxhcd_urb_dequeue(struct usb_hcd *_syshcd, struct urb *_urb)
#else
int ifxhcd_urb_dequeue(struct usb_hcd *_syshcd, struct urb *_urb, int status)
#endif
{
ifxhcd_hcd_t *ifxhcd;
struct usb_host_endpoint *sysep;
ifxhcd_urbd_t *urbd;
ifxhcd_epqh_t *epqh;
IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD URB Dequeue\n");
#if !defined(__EN_ISOC__)
if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS)
return 0;
#endif
ifxhcd = syshcd_to_ifxhcd(_syshcd);
urbd = (ifxhcd_urbd_t *) _urb->hcpriv;
if(!urbd)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
_urb->status=-ETIMEDOUT;
usb_hcd_giveback_urb(_syshcd, _urb);
#else
// usb_hcd_giveback_urb(_syshcd, _urb,-ETIMEDOUT);
usb_hcd_giveback_urb(_syshcd, _urb,status);
#endif
return 0;
}
sysep = ifxhcd_urb_to_endpoint(_urb);
if(sysep)
{
LOCK_EPQH_LIST_ALL(ifxhcd);
epqh = sysep_to_epqh(ifxhcd,sysep);
UNLOCK_EPQH_LIST_ALL(ifxhcd);
if(epqh!=urbd->epqh)
IFX_ERROR("%s inconsistant epqh %p %p\n",__func__,epqh,urbd->epqh);
}
else
epqh = (ifxhcd_epqh_t *) urbd->epqh;
if(!ifxhcd->flags.b.port_connect_status || !epqh)
{
urbd->phase=URBD_DEQUEUEING;
ifxhcd_complete_urb(ifxhcd, urbd, -ENODEV);
}
else
{
LOCK_URBD_LIST(epqh);
if( urbd->phase==URBD_IDLE
|| urbd->phase==URBD_ACTIVE
// || urbd->phase==URBD_STARTING
)
{
urbd->phase=URBD_DEQUEUEING;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
ifxhcd_complete_urb(ifxhcd, urbd, -ETIMEDOUT);
#else
ifxhcd_complete_urb(ifxhcd, urbd, status);
#endif
}
else if( urbd->phase==URBD_STARTED
|| urbd->phase==URBD_STARTING
// || urbd->phase==URBD_ACTIVE
)
{
if(ifxhcd_hc_halt(&ifxhcd->core_if, epqh->hc, HC_XFER_URB_DEQUEUE))
{
urbd->phase=URBD_DEQUEUEING;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
ifxhcd_complete_urb(ifxhcd, urbd, -ETIMEDOUT);
#else
ifxhcd_complete_urb(ifxhcd, urbd, status);
#endif
ifxhcd_epqh_idle(epqh);
}
}
UNLOCK_URBD_LIST(epqh);
}
return 0;
}
/*!
\brief Frees resources in the IFXUSB 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.
*/
void ifxhcd_endpoint_disable( struct usb_hcd *_syshcd,
struct usb_host_endpoint *_sysep)
{
ifxhcd_hcd_t *ifxhcd;
ifxhcd_epqh_t *epqh;
IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD EP DISABLE: _bEndpointAddress=0x%02x, "
"endpoint=%d\n", _sysep->desc.bEndpointAddress,
ifxhcd_ep_addr_to_endpoint(_sysep->desc.bEndpointAddress));
ifxhcd = syshcd_to_ifxhcd(_syshcd);
LOCK_EPQH_LIST_ALL(ifxhcd);
epqh = sysep_to_epqh(ifxhcd,_sysep);
UNLOCK_EPQH_LIST_ALL(ifxhcd);
if (!epqh)
{
return;
}
else
{
if (epqh->sysep!=_sysep)
{
IFX_ERROR("%s inconsistant sysep %p %p %p\n",__func__,epqh,epqh->sysep,_sysep);
return;
}
epqh->phase=EPQH_DISABLING;
kill_all_urbs_in_epqh(ifxhcd, epqh, -ETIMEDOUT);
{
uint32_t count=0x80000;
for(;!list_empty(&epqh->urbd_list) && count> 0; count--) udelay(1);
if(!count)
IFX_ERROR("%s: unable to clear urbd in epqh \n",__func__);
}
ifxhcd_epqh_free(epqh);
}
IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD EP DISABLE: done\n");
}
/*!
\brief Handles host mode interrupts for the IFXUSB 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
*/
irqreturn_t ifxhcd_irq(struct usb_hcd *_syshcd)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd);
int32_t retval=0;
//mask_and_ack_ifx_irq (ifxhcd->core_if.irq);
retval = ifxhcd_handle_intr(ifxhcd);
return IRQ_RETVAL(retval);
}
/*!
\brief 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.
*/
int ifxhcd_hub_status_data(struct usb_hcd *_syshcd, char *_buf)
{
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd);
_buf[0] = 0;
_buf[0] |= (ifxhcd->flags.b.port_connect_status_change ||
ifxhcd->flags.b.port_reset_change ||
ifxhcd->flags.b.port_enable_change ||
ifxhcd->flags.b.port_suspend_change ||
ifxhcd->flags.b.port_over_current_change) << 1;
#ifdef __DEBUG__
if (_buf[0])
{
IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD HUB STATUS DATA:"
" Root port status changed\n");
IFX_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n",
ifxhcd->flags.b.port_connect_status_change);
IFX_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n",
ifxhcd->flags.b.port_reset_change);
IFX_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n",
ifxhcd->flags.b.port_enable_change);
IFX_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n",
ifxhcd->flags.b.port_suspend_change);
IFX_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n",
ifxhcd->flags.b.port_over_current_change);
{
hprt0_data_t hprt0;
hprt0.d32 = ifxusb_rreg(ifxhcd->core_if.hprt0);
IFX_DEBUGPL(DBG_HCDV, " port reg :%08X\n",hprt0.d32);
IFX_DEBUGPL(DBG_HCDV, " port reg :connect: %d/%d\n",hprt0.b.prtconnsts,hprt0.b.prtconndet);
IFX_DEBUGPL(DBG_HCDV, " port reg :enable: %d/%d\n",hprt0.b.prtena,hprt0.b.prtenchng);
IFX_DEBUGPL(DBG_HCDV, " port reg :OC: %d/%d\n",hprt0.b.prtovrcurract,hprt0.b.prtovrcurrchng);
IFX_DEBUGPL(DBG_HCDV, " port reg :rsume/suspend/reset: %d/%d/%d\n",hprt0.b.prtres,hprt0.b.prtsusp,hprt0.b.prtrst);
IFX_DEBUGPL(DBG_HCDV, " port reg :port power: %d/\n",hprt0.b.prtpwr);
IFX_DEBUGPL(DBG_HCDV, " port reg :speed: %d/\n",hprt0.b.prtspd);
}
}
#endif //__DEBUG__
return (_buf[0] != 0);
}
#ifdef __WITH_HS_ELECT_TST__
extern void do_setup(ifxusb_core_if_t *_core_if) ;
extern void do_in_ack(ifxusb_core_if_t *_core_if);
#endif //__WITH_HS_ELECT_TST__
/*!
\brief Handles hub class-specific requests.
*/
int ifxhcd_hub_control( struct usb_hcd *_syshcd,
u16 _typeReq,
u16 _wValue,
u16 _wIndex,
char *_buf,
u16 _wLength)
{
int retval = 0;
ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd);
ifxusb_core_if_t *core_if = &ifxhcd->core_if;
struct usb_hub_descriptor *desc;
hprt0_data_t hprt0 = {.d32 = 0};
uint32_t port_status;
switch (_typeReq)
{
case ClearHubFeature:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearHubFeature 0x%x\n", _wValue);
switch (_wValue)
{
case C_HUB_LOCAL_POWER:
case C_HUB_OVER_CURRENT:
/* Nothing required here */
break;
default:
retval = -EINVAL;
IFX_ERROR ("IFXUSB HCD - "
"ClearHubFeature request %xh unknown\n", _wValue);
}
break;
case ClearPortFeature:
if (!_wIndex || _wIndex > 1)
goto error;
switch (_wValue)
{
case USB_PORT_FEAT_ENABLE:
IFX_DEBUGPL (DBG_ANY, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_ENABLE\n");
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtena = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
break;
case USB_PORT_FEAT_SUSPEND:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtres = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
/* Clear Resume bit */
mdelay (100);
hprt0.b.prtres = 0;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
break;
case USB_PORT_FEAT_POWER:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_POWER\n");
#ifdef __IS_DUAL__
ifxusb_vbus_off(core_if);
#else
ifxusb_vbus_off(core_if);
#endif
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtpwr = 0;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
break;
case USB_PORT_FEAT_INDICATOR:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_INDICATOR\n");
/* Port inidicator not supported */
break;
case USB_PORT_FEAT_C_CONNECTION:
/* Clears drivers internal connect status change
* flag */
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n");
ifxhcd->flags.b.port_connect_status_change = 0;
break;
case USB_PORT_FEAT_C_RESET:
/* Clears the driver's internal Port Reset Change
* flag */
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_C_RESET\n");
ifxhcd->flags.b.port_reset_change = 0;
break;
case USB_PORT_FEAT_C_ENABLE:
/* Clears the driver's internal Port
* Enable/Disable Change flag */
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_C_ENABLE\n");
ifxhcd->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 */
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n");
ifxhcd->flags.b.port_suspend_change = 0;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n");
ifxhcd->flags.b.port_over_current_change = 0;
break;
default:
retval = -EINVAL;
IFX_ERROR ("IFXUSB HCD - "
"ClearPortFeature request %xh "
"unknown or unsupported\n", _wValue);
}
break;
case GetHubDescriptor:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"GetHubDescriptor\n");
desc = (struct usb_hub_descriptor *)_buf;
desc->bDescLength = 9;
desc->bDescriptorType = 0x29;
desc->bNbrPorts = 1;
desc->wHubCharacteristics = 0x08;
desc->bPwrOn2PwrGood = 1;
desc->bHubContrCurrent = 0;
desc->u.hs.DeviceRemovable[0] = 0;
desc->u.hs.DeviceRemovable[1] = 1;
/*desc->bitmap[0] = 0;
desc->bitmap[1] = 0xff;*/
break;
case GetHubStatus:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"GetHubStatus\n");
memset (_buf, 0, 4);
break;
case GetPortStatus:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"GetPortStatus\n");
if (!_wIndex || _wIndex > 1)
goto error;
port_status = 0;
if (ifxhcd->flags.b.port_connect_status_change)
port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
if (ifxhcd->flags.b.port_enable_change)
port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
if (ifxhcd->flags.b.port_suspend_change)
port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
if (ifxhcd->flags.b.port_reset_change)
port_status |= (1 << USB_PORT_FEAT_C_RESET);
if (ifxhcd->flags.b.port_over_current_change)
{
IFX_ERROR("Device Not Supported\n");
port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT);
}
if (!ifxhcd->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.
*/
*((u32 *) _buf) = cpu_to_le32(port_status);
break;
}
hprt0.d32 = ifxusb_rreg(core_if->hprt0);
IFX_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32);
if (hprt0.b.prtconnsts)
port_status |= (1 << USB_PORT_FEAT_CONNECTION);
if (hprt0.b.prtena)
{
ifxhcd->disconnecting=0;
port_status |= (1 << USB_PORT_FEAT_ENABLE);
}
if (hprt0.b.prtsusp)
port_status |= (1 << USB_PORT_FEAT_SUSPEND);
if (hprt0.b.prtovrcurract)
port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT);
if (hprt0.b.prtrst)
port_status |= (1 << USB_PORT_FEAT_RESET);
if (hprt0.b.prtpwr)
port_status |= (1 << USB_PORT_FEAT_POWER);
if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED)
port_status |= USB_PORT_STAT_HIGH_SPEED;
else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED)
port_status |= USB_PORT_STAT_LOW_SPEED;
if (hprt0.b.prttstctl)
port_status |= (1 << USB_PORT_FEAT_TEST);
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
*((u32 *) _buf) = cpu_to_le32(port_status);
break;
case SetHubFeature:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetHubFeature\n");
/* No HUB features supported */
break;
case SetPortFeature:
if (_wValue != USB_PORT_FEAT_TEST && (!_wIndex || _wIndex > 1))
goto error;
/*
* 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.
*/
if (!ifxhcd->flags.b.port_connect_status)
break;
switch (_wValue)
{
case USB_PORT_FEAT_SUSPEND:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtsusp = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
//IFX_PRINT( "SUSPEND: HPRT0=%0x\n", hprt0.d32);
/* Suspend the Phy Clock */
{
pcgcctl_data_t pcgcctl = {.d32=0};
pcgcctl.b.stoppclk = 1;
ifxusb_wreg(core_if->pcgcctl, pcgcctl.d32);
}
break;
case USB_PORT_FEAT_POWER:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetPortFeature - USB_PORT_FEAT_POWER\n");
ifxusb_vbus_on (core_if);
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtpwr = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
break;
case USB_PORT_FEAT_RESET:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetPortFeature - USB_PORT_FEAT_RESET\n");
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtrst = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
MDELAY (60);
hprt0.b.prtrst = 0;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
break;
#ifdef __WITH_HS_ELECT_TST__
case USB_PORT_FEAT_TEST:
{
uint32_t t;
gint_data_t gintmsk;
t = (_wIndex >> 8); /* MSB wIndex USB */
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetPortFeature - USB_PORT_FEAT_TEST %d\n", t);
warn("USB_PORT_FEAT_TEST %d\n", t);
if (t < 6)
{
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prttstctl = t;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
}
else if (t == 6) /* HS_HOST_PORT_SUSPEND_RESUME */
{
/* Save current interrupt mask */
gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk);
/* Disable all interrupts while we muck with
* the hardware directly
*/
ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0);
/* 15 second delay per the test spec */
mdelay(15000);
/* Drive suspend on the root port */
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtsusp = 1;
hprt0.b.prtres = 0;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
/* 15 second delay per the test spec */
mdelay(15000);
/* Drive resume on the root port */
hprt0.d32 = ifxusb_read_hprt0 (core_if);
hprt0.b.prtsusp = 0;
hprt0.b.prtres = 1;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
mdelay(100);
/* Clear the resume bit */
hprt0.b.prtres = 0;
ifxusb_wreg(core_if->hprt0, hprt0.d32);
/* Restore interrupts */
ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32);
}
else if (t == 7) /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */
{
/* Save current interrupt mask */
gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk);
/* Disable all interrupts while we muck with
* the hardware directly
*/
ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0);
/* 15 second delay per the test spec */
mdelay(15000);
/* Send the Setup packet */
do_setup(core_if);
/* 15 second delay so nothing else happens for awhile */
mdelay(15000);
/* Restore interrupts */
ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32);
}
else if (t == 8) /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */
{
/* Save current interrupt mask */
gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk);
/* Disable all interrupts while we muck with
* the hardware directly
*/
ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0);
/* Send the Setup packet */
do_setup(core_if);
/* 15 second delay so nothing else happens for awhile */
mdelay(15000);
/* Send the In and Ack packets */
do_in_ack(core_if);
/* 15 second delay so nothing else happens for awhile */
mdelay(15000);
/* Restore interrupts */
ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32);
}
}
break;
#endif //__WITH_HS_ELECT_TST__
case USB_PORT_FEAT_INDICATOR:
IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - "
"SetPortFeature - USB_PORT_FEAT_INDICATOR\n");
/* Not supported */
break;
default:
retval = -EINVAL;
IFX_ERROR ("IFXUSB HCD - "
"SetPortFeature request %xh "
"unknown or unsupported\n", _wValue);
}
break;
default:
error:
retval = -EINVAL;
IFX_WARN ("IFXUSB HCD - "
"Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n",
_typeReq, _wIndex, _wValue);
}
return retval;
}
/*!
\brief This function trigger a data transfer for a host channel and
starts the transfer.
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.
\param _core_if Pointer of core_if structure
\param _ifxhc Information needed to initialize the host channel. The xfer_len
value may be reduced to accommodate the max widths of the XferSize and
PktCnt fields in the HCTSIZn register. The multi_count value may be changed
to reflect the final xfer_len value.
*/
void ifxhcd_hc_start(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc)
{
ifxusb_core_if_t *core_if = &_ifxhcd->core_if;
uint32_t max_hc_xfer_size = core_if->params.max_transfer_size;
uint16_t max_hc_pkt_count = core_if->params.max_packet_count;
ifxusb_hc_regs_t *hc_regs = core_if->hc_regs[_ifxhc->hc_num];
hfnum_data_t hfnum;
hprt0_data_t hprt0;
if(_ifxhc->epqh->urbd->phase==URBD_DEQUEUEING)
return;
hprt0.d32 = ifxusb_read_hprt0(core_if);
if(_ifxhcd->pkt_remaining==0)
return;
#if 0
if(_ifxhc->phase!=HC_WAITING)
printk(KERN_INFO "%s() line %d: _ifxhc->phase!=HC_WAITING :%d\n",__func__,__LINE__,_ifxhc->phase);
if(_ifxhc->epqh->urbd->phase==URBD_IDLE ) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_IDLE\n",__func__,__LINE__);
// if(_ifxhc->epqh->urbd->phase==URBD_ACTIVE ) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_ACTIVE\n",__func__,__LINE__);
if(_ifxhc->epqh->urbd->phase==URBD_STARTING ) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_STARTING\n",__func__,__LINE__);
if(_ifxhc->epqh->urbd->phase==URBD_STARTED ) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_STARTED\n",__func__,__LINE__);
if(_ifxhc->epqh->urbd->phase==URBD_COMPLETING) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_COMPLETING\n",__func__,__LINE__);
if(_ifxhc->epqh->urbd->phase==URBD_DEQUEUEING) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_DEQUEUEING\n",__func__,__LINE__);
if(_ifxhc->epqh->urbd->phase==URBD_FINISHING ) printk(KERN_INFO "%s() line %d: _ifxhc->epqh->urbd->phase==URBD_FINISHING\n",__func__,__LINE__);
#endif
if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED)
{
if (_ifxhc->split)
{
if(max_hc_pkt_count > _ifxhcd->pkt_remaining)
max_hc_pkt_count = _ifxhcd->pkt_remaining;
}
else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK)
{
if( _ifxhc->is_in && _ifxhcd->pkt_count_limit_bi && max_hc_pkt_count > _ifxhcd->pkt_count_limit_bi)
max_hc_pkt_count = _ifxhcd->pkt_count_limit_bi;
if(!_ifxhc->is_in && _ifxhcd->pkt_count_limit_bo && max_hc_pkt_count > _ifxhcd->pkt_count_limit_bo)
max_hc_pkt_count = _ifxhcd->pkt_count_limit_bo;
if(max_hc_pkt_count*8 > _ifxhcd->pkt_remaining)
max_hc_pkt_count = _ifxhcd->pkt_remaining/8;
}
else
{
if(max_hc_pkt_count > _ifxhcd->pkt_remaining)
max_hc_pkt_count = _ifxhcd->pkt_remaining;
}
}
else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED)
{
if(max_hc_pkt_count > _ifxhcd->pkt_remaining)
max_hc_pkt_count = _ifxhcd->pkt_remaining;
}
else
{
if(max_hc_pkt_count > _ifxhcd->pkt_remaining)
max_hc_pkt_count = _ifxhcd->pkt_remaining;
}
if(max_hc_pkt_count==0)
return;
if(max_hc_pkt_count * _ifxhc->mps < max_hc_xfer_size)
max_hc_xfer_size = max_hc_pkt_count * _ifxhc->mps;
_ifxhc->epqh->urbd->phase=URBD_STARTING;
if(_ifxhc->is_in || _ifxhc->speed != IFXUSB_EP_SPEED_HIGH || _ifxhc->xfer_len==0)
_ifxhc->epqh->do_ping=0;
if(_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR || _ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)
_ifxhc->epqh->do_ping=0;
if(_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL && _ifxhc->control_phase != IFXHCD_CONTROL_DATA )
_ifxhc->epqh->do_ping=0;
if (_ifxhc->split > 0)
{
_ifxhc->start_pkt_count = 1;
if(!_ifxhc->is_in && _ifxhc->split>1) // OUT CSPLIT
_ifxhc->xfer_len = 0;
if (_ifxhc->xfer_len > _ifxhc->mps)
_ifxhc->xfer_len = _ifxhc->mps;
if (_ifxhc->xfer_len > 188)
_ifxhc->xfer_len = 188;
}
else if(_ifxhc->is_in)
{
_ifxhc->short_rw = 0;
if (_ifxhc->xfer_len > 0)
{
if (_ifxhc->xfer_len > max_hc_xfer_size)
_ifxhc->xfer_len = max_hc_xfer_size - _ifxhc->mps + 1;
_ifxhc->start_pkt_count = (_ifxhc->xfer_len + _ifxhc->mps - 1) / _ifxhc->mps;
if (_ifxhc->start_pkt_count > max_hc_pkt_count)
_ifxhc->start_pkt_count = max_hc_pkt_count;
}
else /* Need 1 packet for transfer length of 0. */
_ifxhc->start_pkt_count = 1;
_ifxhc->xfer_len = _ifxhc->start_pkt_count * _ifxhc->mps;
}
else //non-split out
{
if (_ifxhc->xfer_len == 0)
{
if(_ifxhc->short_rw==0)
printk(KERN_INFO "Info: %s() line %d: ZLP write without short_rw set! xfer_count:%d/%d \n",__func__,__LINE__,
_ifxhc->xfer_count,
_ifxhc->epqh->urbd->xfer_len);
_ifxhc->start_pkt_count = 1;
}
else
{
if (_ifxhc->xfer_len > max_hc_xfer_size)
{
_ifxhc->start_pkt_count = (max_hc_xfer_size / _ifxhc->mps);
_ifxhc->xfer_len = _ifxhc->start_pkt_count * _ifxhc->mps;
}
else
{
_ifxhc->start_pkt_count = (_ifxhc->xfer_len+_ifxhc->mps-1) / _ifxhc->mps;
// if(_ifxhc->start_pkt_count * _ifxhc->mps == _ifxhc->xfer_len )
// _ifxhc->start_pkt_count += _ifxhc->short_rw;
}
}
}
if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED)
{
if (_ifxhc->split)
{
if( _ifxhcd->pkt_remaining > _ifxhc->start_pkt_count)
_ifxhcd->pkt_remaining -= _ifxhc->start_pkt_count;
else
_ifxhcd->pkt_remaining = 0;
}
else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK)
{
if( _ifxhcd->pkt_remaining*8 > _ifxhc->start_pkt_count)
_ifxhcd->pkt_remaining -= (_ifxhc->start_pkt_count*8);
else
_ifxhcd->pkt_remaining = 0;
}
else
{
if( _ifxhcd->pkt_remaining > _ifxhc->start_pkt_count)
_ifxhcd->pkt_remaining -= _ifxhc->start_pkt_count;
else
_ifxhcd->pkt_remaining = 0;
}
}
else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED)
{
if( _ifxhcd->pkt_remaining > _ifxhc->start_pkt_count)
_ifxhcd->pkt_remaining -= _ifxhc->start_pkt_count;
else
_ifxhcd->pkt_remaining = 0;
}
else
{
if( _ifxhcd->pkt_remaining > _ifxhc->start_pkt_count)
_ifxhcd->pkt_remaining -= _ifxhc->start_pkt_count;
else
_ifxhcd->pkt_remaining = 0;
}
#ifdef __EN_ISOC__
if (_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)
{
/* Set up the initial PID for the transfer. */
#if 1
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0;
#else
if (_ifxhc->speed == IFXUSB_EP_SPEED_HIGH)
{
if (_ifxhc->is_in)
{
if (_ifxhc->multi_count == 1)
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0;
else if (_ifxhc->multi_count == 2)
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1;
else
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA2;
}
else
{
if (_ifxhc->multi_count == 1)
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0;
else
_ifxhc->data_pid_start = IFXUSB_HC_PID_MDATA;
}
}
else
_ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0;
#endif
}
#endif
IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _ifxhc->hc_num);
{
hctsiz_data_t hctsiz= { .d32=0 };
hctsiz.b.dopng = _ifxhc->epqh->do_ping;
_ifxhc->epqh->do_ping=0;
if(_ifxhc->is_in || _ifxhc->speed != IFXUSB_EP_SPEED_HIGH || _ifxhc->xfer_len==0)
hctsiz.b.dopng = 0;
if(_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR || _ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)
hctsiz.b.dopng = 0;
if(_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL && _ifxhc->control_phase != IFXHCD_CONTROL_DATA )
hctsiz.b.dopng = 0;
hctsiz.b.xfersize = _ifxhc->xfer_len;
hctsiz.b.pktcnt = _ifxhc->start_pkt_count;
hctsiz.b.pid = _ifxhc->data_pid_start;
ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32);
IFX_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize);
IFX_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n" , hctsiz.b.pktcnt);
IFX_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid);
}
IFX_DEBUGPL(DBG_HCDV, " DMA: 0x%08x\n", (uint32_t)(CPHYSADDR( ((uint32_t)(_ifxhc->xfer_buff))+ _ifxhc->xfer_count )));
ifxusb_wreg(&hc_regs->hcdma, (uint32_t)(CPHYSADDR( ((uint32_t)(_ifxhc->xfer_buff))+ _ifxhc->xfer_count )));
/* Start the split */
if (_ifxhc->split>0)
{
hcsplt_data_t hcsplt;
hcsplt.d32 = ifxusb_rreg (&hc_regs->hcsplt);
hcsplt.b.spltena = 1;
if (_ifxhc->split>1)
hcsplt.b.compsplt = 1;
else
hcsplt.b.compsplt = 0;
#if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__)
if (_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)
hcsplt.b.xactpos = _ifxhc->isoc_xact_pos;
else
#endif
hcsplt.b.xactpos = IFXUSB_HCSPLIT_XACTPOS_ALL;// if not ISO
ifxusb_wreg(&hc_regs->hcsplt, hcsplt.d32);
IFX_DEBUGPL(DBG_HCDV, " SPLIT: XACT_POS:0x%08x\n", hcsplt.d32);
}
{
hcchar_data_t hcchar;
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
// hcchar.b.multicnt = _ifxhc->multi_count;
hcchar.b.multicnt = 1;
if (_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR || _ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)
{
hfnum.d32 = ifxusb_rreg(&core_if->host_global_regs->hfnum);
/* 1 if _next_ frame is odd, 0 if it's even */
hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;
}
#ifdef __DEBUG__
_ifxhc->start_hcchar_val = hcchar.d32;
if (hcchar.b.chdis)
IFX_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n",
__func__, _ifxhc->hc_num, hcchar.d32);
#endif
/* Set host channel enable after all other setup is complete. */
hcchar.b.chen = 1;
hcchar.b.chdis = 0;
hcchar.b.epdir = _ifxhc->is_in;
_ifxhc->hcchar=hcchar.d32;
}
IFX_DEBUGPL(DBG_HCDV, " HCCHART: 0x%08x\n", _ifxhc->hcchar);
_ifxhc->phase=HC_STARTING;
}
/*!
\brief Attempts to halt a host channel. This function should only be called
to abort a transfer in 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 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.
*/
int ifxhcd_hc_halt(ifxusb_core_if_t *_core_if,
ifxhcd_hc_t *_ifxhc,
ifxhcd_halt_status_e _halt_status)
{
hcchar_data_t hcchar;
ifxusb_hc_regs_t *hc_regs;
hc_regs = _core_if->hc_regs[_ifxhc->hc_num];
WARN_ON(_halt_status == HC_XFER_NO_HALT_STATUS);
{
hprt0_data_t hprt0;
hprt0.d32 = ifxusb_rreg(_core_if->hprt0);
if(hprt0.b.prtena == 0)
return -1;
}
if (_halt_status == HC_XFER_URB_DEQUEUE ||
_halt_status == HC_XFER_AHB_ERR)
{
/*
* Disable all channel interrupts except Ch Halted. The URBD
* and EPQH 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.
*/
hcint_data_t hcintmsk;
hcintmsk.d32 = 0;
hcintmsk.b.chhltd = 1;
ifxusb_wreg(&hc_regs->hcintmsk, hcintmsk.d32);
/*
* Make sure no other interrupts besides halt are currently
* pending. Handling another interrupt could cause a crash due
* to the URBD and EPQH state.
*/
ifxusb_wreg(&hc_regs->hcint, ~hcintmsk.d32);
/*
* Make sure the halt status is set to URB_DEQUEUE or AHB_ERR
* even if the channel was already halted for some other
* reason.
*/
_ifxhc->halt_status = _halt_status;
}
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
if (hcchar.b.chen == 0)
{
/*
* 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.
*/
_ifxhc->phase=HC_IDLE;
return -1;
}
if (_ifxhc->phase==HC_STOPPING)
{
/*
* A halt has already been issued for this channel. This might
* happen when a transfer is aborted by a higher level in
* the stack.
*/
#ifdef __DEBUG__
IFX_PRINT("*** %s: Channel %d, double halt a channel***\n",
__func__, _ifxhc->hc_num);
#endif
return 0;
}
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
hcchar.b.chen = 1;
hcchar.b.chdis = 1;
ifxusb_wreg(&hc_regs->hcchar, hcchar.d32);
_ifxhc->halt_status = _halt_status;
_ifxhc->phase=HC_STOPPING;
IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n" , __func__, _ifxhc->hc_num);
IFX_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n" , hcchar.d32);
IFX_DEBUGPL(DBG_HCDV, " halt_status: %d\n" , _ifxhc->halt_status);
return 0;
}
/*!
\brief Clears a host channel.
*/
void ifxhcd_hc_cleanup(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc)
{
ifxusb_hc_regs_t *hc_regs;
_ifxhc->phase=HC_IDLE;
_ifxhc->epqh=0;
/*
* Clear channel interrupt enables and any unhandled channel interrupt
* conditions.
*/
hc_regs = _core_if->hc_regs[_ifxhc->hc_num];
ifxusb_wreg(&hc_regs->hcintmsk, 0);
ifxusb_wreg(&hc_regs->hcint, 0xFFFFFFFF);
#ifdef __DEBUG__
{
hcchar_data_t hcchar;
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
if (hcchar.b.chdis)
IFX_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", __func__, _ifxhc->hc_num, hcchar.d32);
}
#endif
}
#ifdef __DEBUG__
static void dump_urb_info(struct urb *_urb, char* _fn_name)
{
IFX_PRINT("%s, urb %p\n" , _fn_name, _urb);
IFX_PRINT(" Device address: %d\n", usb_pipedevice(_urb->pipe));
IFX_PRINT(" Endpoint: %d, %s\n" , usb_pipeendpoint(_urb->pipe),
(usb_pipein(_urb->pipe) ? "IN" : "OUT"));
IFX_PRINT(" Endpoint type: %s\n",
({ char *pipetype;
switch (usb_pipetype(_urb->pipe)) {
case PIPE_CONTROL: pipetype = "CONTROL"; break;
case PIPE_BULK: pipetype = "BULK"; break;
case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break;
case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break;
default: pipetype = "UNKNOWN"; break;
};
pipetype;
}));
IFX_PRINT(" Speed: %s\n",
({ char *speed;
switch (_urb->dev->speed) {
case USB_SPEED_HIGH: speed = "HIGH"; break;
case USB_SPEED_FULL: speed = "FULL"; break;
case USB_SPEED_LOW: speed = "LOW"; break;
default: speed = "UNKNOWN"; break;
};
speed;
}));
IFX_PRINT(" Max packet size: %d\n",
usb_maxpacket(_urb->dev, _urb->pipe, usb_pipeout(_urb->pipe)));
IFX_PRINT(" Data buffer length: %d\n", _urb->transfer_buffer_length);
IFX_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n",
_urb->transfer_buffer, (void *)_urb->transfer_dma);
IFX_PRINT(" Setup buffer: %p, Setup DMA: %p\n",
_urb->setup_packet, (void *)_urb->setup_dma);
IFX_PRINT(" Interval: %d\n", _urb->interval);
if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS)
{
int i;
for (i = 0; i < _urb->number_of_packets; i++)
{
IFX_PRINT(" ISO Desc %d:\n", i);
IFX_PRINT(" offset: %d, length %d\n",
_urb->iso_frame_desc[i].offset,
_urb->iso_frame_desc[i].length);
}
}
}
#if 0
static void dump_channel_info(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh)
{
if (_epqh->hc != NULL)
{
ifxhcd_hc_t *hc = _epqh->hc;
struct list_head *item;
ifxhcd_epqh_t *epqh_item;
ifxusb_hc_regs_t *hc_regs;
hcchar_data_t hcchar;
hcsplt_data_t hcsplt;
hctsiz_data_t hctsiz;
uint32_t hcdma;
hc_regs = _ifxhcd->core_if.hc_regs[hc->hc_num];
hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar);
hcsplt.d32 = ifxusb_rreg(&hc_regs->hcsplt);
hctsiz.d32 = ifxusb_rreg(&hc_regs->hctsiz);
hcdma = ifxusb_rreg(&hc_regs->hcdma);
IFX_PRINT(" Assigned to channel %d:\n" , hc->hc_num);
IFX_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32);
IFX_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n" , hctsiz.d32, hcdma);
IFX_PRINT(" dev_addr: %d, ep_num: %d, is_in: %d\n",
hc->dev_addr, hc->ep_num, hc->is_in);
IFX_PRINT(" ep_type: %d\n" , hc->ep_type);
IFX_PRINT(" max_packet_size: %d\n", hc->mps);
IFX_PRINT(" data_pid_start: %d\n" , hc->data_pid_start);
IFX_PRINT(" halt_status: %d\n" , hc->halt_status);
IFX_PRINT(" xfer_buff: %p\n" , hc->xfer_buff);
IFX_PRINT(" xfer_len: %d\n" , hc->xfer_len);
IFX_PRINT(" epqh: %p\n" , hc->epqh);
IFX_PRINT(" NP :\n");
list_for_each(item, &_ifxhcd->epqh_list_np)
{
epqh_item = list_entry(item, ifxhcd_epqh_t, ql);
IFX_PRINT(" %p\n", epqh_item);
}
IFX_PRINT(" INTR :\n");
list_for_each(item, &_ifxhcd->epqh_list_intr)
{
epqh_item = list_entry(item, ifxhcd_epqh_t, ql);
IFX_PRINT(" %p\n", epqh_item);
}
#ifdef __EN_ISOC__
IFX_PRINT(" ISOC:\n");
list_for_each(item, &_ifxhcd->epqh_list_isoc)
{
epqh_item = list_entry(item, ifxhcd_epqh_t, ql);
IFX_PRINT(" %p\n", epqh_item);
}
#endif
}
}
#endif
#endif //__DEBUG__
/*!
\brief 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.
*/
#ifdef __ENABLE_DUMP__
void ifxhcd_dump_state(ifxhcd_hcd_t *_ifxhcd)
{
int num_channels;
int i;
num_channels = _ifxhcd->core_if.params.host_channels;
IFX_PRINT("\n");
IFX_PRINT("************************************************************\n");
IFX_PRINT("HCD State:\n");
IFX_PRINT(" Num channels: %d\n", num_channels);
for (i = 0; i < num_channels; i++) {
ifxhcd_hc_t *hc = &_ifxhcd->ifxhc[i];
IFX_PRINT(" Channel %d:\n", hc->hc_num);
IFX_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n",
hc->dev_addr, hc->ep_num, hc->is_in);
IFX_PRINT(" speed: %d\n" , hc->speed);
IFX_PRINT(" ep_type: %d\n" , hc->ep_type);
IFX_PRINT(" mps: %d\n", hc->mps);
IFX_PRINT(" data_pid_start: %d\n" , hc->data_pid_start);
IFX_PRINT(" xfer_buff: %p\n" , hc->xfer_buff);
IFX_PRINT(" xfer_len: %d\n" , hc->xfer_len);
IFX_PRINT(" xfer_count: %d\n" , hc->xfer_count);
IFX_PRINT(" halt_status: %d\n" , hc->halt_status);
IFX_PRINT(" split: %d\n" , hc->split);
IFX_PRINT(" hub_addr: %d\n" , hc->hub_addr);
IFX_PRINT(" port_addr: %d\n" , hc->port_addr);
#if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__)
IFX_PRINT(" isoc_xact_pos: %d\n" , hc->isoc_xact_pos);
#endif
IFX_PRINT(" epqh: %p\n" , hc->epqh);
IFX_PRINT(" short_rw: %d\n" , hc->short_rw);
IFX_PRINT(" control_phase: %d\n" , hc->control_phase);
if(hc->epqh)
{
IFX_PRINT(" do_ping: %d\n" , hc->epqh->do_ping);
}
IFX_PRINT(" start_pkt_count: %d\n" , hc->start_pkt_count);
}
IFX_PRINT("************************************************************\n");
IFX_PRINT("\n");
}
#endif //__ENABLE_DUMP__