X-Git-Url: https://git.archive.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Flantiq%2Ffiles%2Fdrivers%2Fusb%2Fifxhcd%2Fifxhcd.c;fp=target%2Flinux%2Flantiq%2Ffiles%2Fdrivers%2Fusb%2Fifxhcd%2Fifxhcd.c;h=d2ae1250bb9bdbc37c83c2c5ce439f92899f1e06;hb=cea2b4210d9b3706cad3cc60cc54dde063e09b58;hp=0000000000000000000000000000000000000000;hpb=6b899d5deac5b0ad531d7a7f2d1d241727848535;p=openwrt.git diff --git a/target/linux/lantiq/files/drivers/usb/ifxhcd/ifxhcd.c b/target/linux/lantiq/files/drivers/usb/ifxhcd/ifxhcd.c new file mode 100644 index 0000000000..d2ae1250bb --- /dev/null +++ b/target/linux/lantiq/files/drivers/usb/ifxhcd/ifxhcd.c @@ -0,0 +1,2523 @@ +/***************************************************************************** + ** FILE NAME : ifxhcd.c + ** PROJECT : IFX USB sub-system V3 + ** MODULES : IFX USB sub-system Host and Device driver + ** SRC VERSION : 1.0 + ** DATE : 1/Jan/2009 + ** 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. + *****************************************************************************/ + +/*! + \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 +#include "ifxusb_version.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + + +#include "ifxusb_plat.h" +#include "ifxusb_regs.h" +#include "ifxusb_cif.h" +#include "ifxhcd.h" + +#include + +#ifdef CONFIG_AVM_POWERMETER +#include +#endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ + +#ifdef __DEBUG__ + static void dump_urb_info(struct urb *_urb, char* _fn_name); + static void dump_channel_info(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh); +#endif + + +/*! + \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) +{ + struct urb *urb=NULL; + unsigned long flags = 0; + + /*== AVM/BC 20101111 Function called with Lock ==*/ + //SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); + + if (!list_empty(&_urbd->urbd_list_entry)) + list_del_init (&_urbd->urbd_list_entry); + + if(!_urbd->urb) + { + IFX_ERROR("%s: invalid urb\n",__func__); + /*== AVM/BC 20101111 Function called with Lock ==*/ + //SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); + return; + } + + urb=_urbd->urb; + + #ifdef __DEBUG__ + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) + { + IFX_PRINT("%s: _urbd %p, urb %p, device %d, ep %d %s/%s, status=%d\n", + __func__, _urbd,_urbd->urb, usb_pipedevice(_urbd->urb->pipe), + usb_pipeendpoint(_urbd->urb->pipe), + usb_pipein(_urbd->urb->pipe) ? "IN" : "OUT", + (_urbd->is_in) ? "IN" : "OUT", + _status); + if (_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 + + if (!_urbd->epqh) + IFX_ERROR("%s: invalid epqd\n",__func__); + + #if defined(__UNALIGNED_BUFFER_ADJ__) + else if(_urbd->is_active) + { + if( _urbd->epqh->aligned_checked && + _urbd->epqh->using_aligned_buf && + _urbd->xfer_buff && + _urbd->is_in ) + memcpy(_urbd->xfer_buff,_urbd->epqh->aligned_buf,_urbd->xfer_len); + _urbd->epqh->using_aligned_buf=0; + _urbd->epqh->using_aligned_setup=0; + _urbd->epqh->aligned_checked=0; + } + #endif + + urb->status = _status; + urb->hcpriv=NULL; + kfree(_urbd); + + usb_hcd_unlink_urb_from_ep(ifxhcd_to_syshcd(_ifxhcd), urb); + SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); + +// usb_hcd_giveback_urb(ifxhcd_to_syshcd(_ifxhcd), urb); + usb_hcd_giveback_urb(ifxhcd_to_syshcd(_ifxhcd), urb, _status); + + /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ + SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); +} + +/*== AVM/BC 20101111 URB Complete deferred + * Must be called with Spinlock + */ + +/*! + \brief Inserts an urbd structur in the completion list. The urbd will be + later completed by select_eps_sub + */ +void defer_ifxhcd_complete_urb(ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status) +{ + + _urbd->status = _status; + + //Unlink Urbd from epqh / Insert it into the complete list + list_move_tail(&_urbd->urbd_list_entry, &_ifxhcd->urbd_complete_list); + +} + +/*! + \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 *urbd_item; + ifxhcd_urbd_t *urbd; + + if(!_epqh) + return; + + for (urbd_item = _epqh->urbd_list.next; + urbd_item != &_epqh->urbd_list; + urbd_item = _epqh->urbd_list.next) + { + urbd = list_entry(urbd_item, ifxhcd_urbd_t, urbd_list_entry); + ifxhcd_complete_urb(_ifxhcd, urbd, _status); + } +} + + +/*! + \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(ifxhcd_hcd_t *_ifxhcd, struct list_head *_epqh_list) +{ + struct list_head *item; + ifxhcd_epqh_t *epqh; + + if (!_epqh_list) + return; + if (_epqh_list->next == NULL) /* The list hasn't been initialized yet. */ + return; + + /* Ensure there are no URBDs or URBs left. */ + for (item = _epqh_list->next; item != _epqh_list; item = _epqh_list->next) + { + epqh = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + kill_all_urbs_in_epqh(_ifxhcd, epqh, -ETIMEDOUT); + ifxhcd_epqh_free(epqh); + } +} + + + +//static +void epqh_list_free_all(ifxhcd_hcd_t *_ifxhcd) +{ + unsigned long flags; + + /*== AVM/BC 20101111 - 2.6.28 Needs Spinlock ==*/ + SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); + + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_np_active ); + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_np_ready ); + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_intr_active ); + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_intr_ready ); + #ifdef __EN_ISOC__ + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_isoc_active ); + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_isoc_ready ); + #endif + epqh_list_free(_ifxhcd, &_ifxhcd->epqh_stdby ); + + SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); + +} + + +/*! + \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); + + /* 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]; + if (list_empty(&channel->hc_list_entry)) + { + hc_regs = _ifxhcd->core_if.hc_regs[i]; + hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); + if (hcchar.b.chen) + { + /* Halt the channel. */ + hcchar.b.chdis = 1; + ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); + } + list_add_tail(&channel->hc_list_entry, &_ifxhcd->free_hc_list); + ifxhcd_hc_cleanup(&_ifxhcd->core_if, channel); + } + } + } + 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(ifxhcd->status_buf); + return; +} +#ifdef __USE_TIMER_4_SOF__ +static enum hrtimer_restart ifxhcd_timer_func(struct hrtimer *timer) { + ifxhcd_hcd_t *ifxhcd = container_of(timer, ifxhcd_hcd_t, hr_timer); + + ifxhcd_handle_intr(ifxhcd); + + return HRTIMER_NORESTART; +} +#endif + +/*! + \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"); + + spin_lock_init(&_ifxhcd->lock); +#ifdef __USE_TIMER_4_SOF__ + hrtimer_init(&_ifxhcd->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + _ifxhcd->hr_timer.function = ifxhcd_timer_func; +#endif + _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 = + + /* Allocate memory for and initialize the base HCD and */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) + syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->core_if.core_name); +#else + syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->dev->bus_id); +#endif + + if (syshcd == NULL) + { + retval = -ENOMEM; + goto error1; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) + syshcd->has_tt = 1; +#endif + + 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_np_active ); + INIT_LIST_HEAD(&_ifxhcd->epqh_np_ready ); + INIT_LIST_HEAD(&_ifxhcd->epqh_intr_active ); + INIT_LIST_HEAD(&_ifxhcd->epqh_intr_ready ); + #ifdef __EN_ISOC__ + INIT_LIST_HEAD(&_ifxhcd->epqh_isoc_active ); + INIT_LIST_HEAD(&_ifxhcd->epqh_isoc_ready ); + #endif + INIT_LIST_HEAD(&_ifxhcd->epqh_stdby ); + INIT_LIST_HEAD(&_ifxhcd->urbd_complete_list); + + /* + * Create a host channel descriptor for each host channel implemented + * in the controller. Initialize the channel descriptor array. + */ + INIT_LIST_HEAD(&_ifxhcd->free_hc_list); + { + 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, SA_INTERRUPT|SA_SHIRQ); + retval = usb_add_hcd(syshcd, _ifxhcd->core_if.irq, IRQF_DISABLED | 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(IFXHCD_STATUS_BUF_SIZE, 1); + + if (_ifxhcd->status_buf) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) + IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Initialized, bus=%s, usbbus=%d\n", _ifxhcd->core_if.core_name, syshcd->self.busnum); +#else + IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Initialized, bus=%s, usbbus=%d\n", _ifxhcd->dev->bus_id, syshcd->self.busnum); +#endif + 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"); + +/* == AVM/WK 20100709 - Fix: Order changed, disable IRQs not before remove_hcd == */ + + usb_remove_hcd(syshcd); + + /* 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); + + 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.*/ + { + struct list_head *item; + item = ifxhcd->free_hc_list.next; + while (item != &ifxhcd->free_hc_list) + { + list_del(item); + item = ifxhcd->free_hc_list.next; + } + } + { + 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]; + list_add_tail(&channel->hc_list_entry, &ifxhcd->free_hc_list); + 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(core_if); + ifxusb_phy_power_on (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. + */ +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(&ifxhcd->core_if ); + ifxusb_host_disable_interrupts(&ifxhcd->core_if ); +#ifdef __USE_TIMER_4_SOF__ + hrtimer_cancel(&ifxhcd->hr_timer); +#endif + /* + * 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 ); + + 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, + /*--- struct usb_host_endpoint *_sysep, Parameter im 2.6.28 entfallen ---*/ + struct urb *_urb, + gfp_t _mem_flags) +{ + int retval = 0; + ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); + struct usb_host_endpoint *_sysep = ifxhcd_urb_to_endpoint(_urb); + ifxhcd_epqh_t *epqh; + + #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; + + #ifndef __EN_ISOC__ + if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) + { + IFX_ERROR("ISOC transfer not supported!!!\n"); + return -ENODEV; + } + #endif + + retval=ifxhcd_urbd_create (ifxhcd,_urb); + + if (retval) + { + IFX_ERROR("IFXUSB HCD URB Enqueue failed creating URBD\n"); + return retval; + } + epqh = (ifxhcd_epqh_t *) _sysep->hcpriv; + ifxhcd_epqh_ready(ifxhcd, epqh); + + select_eps(ifxhcd); + //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); + } + + return retval; +} + +/*! + \brief Aborts/cancels a USB transfer request. Always returns 0 to indicate + success. + */ +int ifxhcd_urb_dequeue( struct usb_hcd *_syshcd, + struct urb *_urb, int status /* Parameter neu in 2.6.28 */) +{ + unsigned long flags; + ifxhcd_hcd_t *ifxhcd; + ifxhcd_urbd_t *urbd; + ifxhcd_epqh_t *epqh; + int is_active=0; + int rc; + + struct usb_host_endpoint *_sysep; + + IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD URB Dequeue\n"); + + #ifndef __EN_ISOC__ + if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) + return 0; + #endif + + _sysep = ifxhcd_urb_to_endpoint(_urb); + + ifxhcd = syshcd_to_ifxhcd(_syshcd); + + SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); + + /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ + rc = usb_hcd_check_unlink_urb(_syshcd, _urb, status); + if (rc) { + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); + return rc; + } + + urbd = (ifxhcd_urbd_t *) _urb->hcpriv; + + if(_sysep) + epqh = (ifxhcd_epqh_t *) _sysep->hcpriv; + else + epqh = (ifxhcd_epqh_t *) urbd->epqh; + + if(epqh!=urbd->epqh) + IFX_ERROR("%s inconsistant epqh %p %p\n",__func__,epqh,urbd->epqh); + + #ifdef __DEBUG__ + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) + { + dump_urb_info(_urb, "ifxhcd_urb_dequeue"); + if (epqh->is_active) + dump_channel_info(ifxhcd, epqh); + } + #endif //__DEBUG__ + + if(!epqh->hc) + epqh->is_active=0; + else if (!ifxhcd->flags.b.port_connect_status) + epqh->is_active=0; + else if (epqh->is_active && urbd->is_active) + { + /*== AVM/WK 20100709 - halt channel only if really started ==*/ + //if (epqh->hc->xfer_started && !epqh->hc->wait_for_sof) { + /*== AVM/WK 20101112 - halt channel if started ==*/ + if (epqh->hc->xfer_started) { + /* + * 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. + */ + /* == 20110803 AVM/WK FIX propagate status == */ + if (_urb->status == -EINPROGRESS) { + _urb->status = status; + } + ifxhcd_hc_halt(&ifxhcd->core_if, epqh->hc, HC_XFER_URB_DEQUEUE); + epqh->hc = NULL; + is_active=1; + } + } + + if(is_active) + { + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); + } + else + { + list_del_init(&urbd->urbd_list_entry); + kfree (urbd); + + /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ + usb_hcd_unlink_urb_from_ep(_syshcd, _urb); + + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); + _urb->hcpriv = NULL; +// usb_hcd_giveback_urb(_syshcd, _urb); + usb_hcd_giveback_urb(_syshcd, _urb, status /* neu in 2.6.28 */); + select_eps(ifxhcd); + } + + 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_epqh_t *epqh; + ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd); + unsigned long flags; + + int retry = 0; + + 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)); + + SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); + if((uint32_t)_sysep>=0x80000000 && (uint32_t)_sysep->hcpriv>=(uint32_t)0x80000000) + { + epqh = (ifxhcd_epqh_t *)(_sysep->hcpriv); + if (epqh && epqh->sysep==_sysep) + { + +#if 1 /*== AVM/BC 20101111 CHG Option active: Kill URBs when disabling EP ==*/ + while (!list_empty(&epqh->urbd_list)) + { + if (retry++ > 250) + { + IFX_WARN("IFXUSB HCD EP DISABLE:" + " URBD List for this endpoint is not empty\n"); + break; + } + kill_all_urbs_in_epqh(ifxhcd, epqh, -ETIMEDOUT); + } +#else + while (!list_empty(&epqh->urbd_list)) + { + /** Check that the QTD list is really empty */ + if (retry++ > 250) + { + IFX_WARN("IFXUSB HCD EP DISABLE:" + " URBD List for this endpoint is not empty\n"); + break; + } + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); + schedule_timeout_uninterruptible(1); + SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); + } +#endif + + ifxhcd_epqh_free(epqh); + _sysep->hcpriv = NULL; + } + } + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); +} + + +/*! + \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 Handles host mode Over Current Interrupt + */ +irqreturn_t ifxhcd_oc_irq(int _irq , void *_dev) +{ + ifxhcd_hcd_t *ifxhcd = _dev; + int32_t retval=1; + + ifxhcd->flags.b.port_over_current_change = 1; + ifxusb_vbus_off(&ifxhcd->core_if); + IFX_DEBUGP("OC INTERRUPT # %d\n",ifxhcd->core_if.core_no); + + //mask_and_ack_ifx_irq (_irq); + 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); + } + #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->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; + +# ifdef CONFIG_AVM_POWERMETER + { + /* first port only, but 2 Hosts */ + static unsigned char ucOldPower1 = 255; + static unsigned char ucOldPower2 = 255; + + unsigned char ucNewPower = 0; + struct usb_device *childdev = _syshcd->self.root_hub->children[0]; + + if (childdev != NULL) { + ucNewPower = (childdev->actconfig != NULL) + ? childdev->actconfig->desc.bMaxPower + : 50;/* default: 50 means 100 mA*/ + } + if (_syshcd->self.busnum == 1) { + if (ucOldPower1 != ucNewPower) { + ucOldPower1 = ucNewPower; + printk (KERN_INFO "IFXHCD#1: AVM Powermeter changed to %u mA\n", ucNewPower*2); + PowerManagmentRessourceInfo(powerdevice_usb_host, ucNewPower*2); + } + } else { + if (ucOldPower2 != ucNewPower) { + ucOldPower2 = ucNewPower; + printk (KERN_INFO "IFXHCD#2: AVM Powermeter changed to %u mA\n", ucNewPower*2); + PowerManagmentRessourceInfo(powerdevice_usb_host2, ucNewPower*2); + } + } + } +# endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ + + 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) + 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 |= (1 << USB_PORT_FEAT_HIGHSPEED); + else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED) + port_status |= (1 << USB_PORT_FEAT_LOWSPEED);*/ + 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 Assigns transactions from a URBD to a free host channel and initializes the + host channel to perform the transactions. The host channel is removed from + the free list. + \param _ifxhcd The HCD state structure. + \param _epqh Transactions from the first URBD for this EPQH are selected and assigned to a free host channel. + */ +static int assign_and_init_hc(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) +{ + ifxhcd_hc_t *ifxhc; + ifxhcd_urbd_t *urbd; + struct urb *urb; + + IFX_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, _ifxhcd, _epqh); + + if(list_empty(&_epqh->urbd_list)) + return 0; + + ifxhc = list_entry(_ifxhcd->free_hc_list.next, ifxhcd_hc_t, hc_list_entry); + /* Remove the host channel from the free list. */ + list_del_init(&ifxhc->hc_list_entry); + + urbd = list_entry(_epqh->urbd_list.next, ifxhcd_urbd_t, urbd_list_entry); + urb = urbd->urb; + + _epqh->hc = ifxhc; + _epqh->urbd = urbd; + ifxhc->epqh = _epqh; + + urbd->is_active=1; + + /* + * Use usb_pipedevice to determine device address. This address is + * 0 before the SET_ADDRESS command and the correct address afterward. + */ + ifxhc->dev_addr = usb_pipedevice(urb->pipe); + ifxhc->ep_num = usb_pipeendpoint(urb->pipe); + + ifxhc->xfer_started = 0; + + if (urb->dev->speed == USB_SPEED_LOW) ifxhc->speed = IFXUSB_EP_SPEED_LOW; + else if (urb->dev->speed == USB_SPEED_FULL) ifxhc->speed = IFXUSB_EP_SPEED_FULL; + else ifxhc->speed = IFXUSB_EP_SPEED_HIGH; + + ifxhc->mps = _epqh->mps; + ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; + + ifxhc->ep_type = _epqh->ep_type; + + if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) + { + ifxhc->control_phase=IFXHCD_CONTROL_SETUP; + ifxhc->is_in = 0; + ifxhc->data_pid_start = IFXUSB_HC_PID_SETUP; + ifxhc->xfer_buff = urbd->setup_buff; + ifxhc->xfer_len = 8; + ifxhc->xfer_count = 0; + ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; + } + else + { + ifxhc->is_in = urbd->is_in; + ifxhc->xfer_buff = urbd->xfer_buff; + ifxhc->xfer_len = urbd->xfer_len; + ifxhc->xfer_count = 0; + /* == AVM/WK 20100710 Fix - Use toggle of usbcore ==*/ + //ifxhc->data_pid_start = _epqh->data_toggle; + ifxhc->data_pid_start = usb_gettoggle (urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout (urb->pipe)) + ? IFXUSB_HC_PID_DATA1 + : IFXUSB_HC_PID_DATA0; + if(ifxhc->is_in) + ifxhc->short_rw =0; + else + ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; + + #ifdef __EN_ISOC__ + if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) + { + struct usb_iso_packet_descriptor *frame_desc; + frame_desc = &urb->iso_frame_desc[urbd->isoc_frame_index]; + ifxhc->xfer_buff += frame_desc->offset + urbd->isoc_split_offset; + ifxhc->xfer_len = frame_desc->length - urbd->isoc_split_offset; + if (ifxhc->isoc_xact_pos == IFXUSB_HCSPLIT_XACTPOS_ALL) + { + if (ifxhc->xfer_len <= 188) + ifxhc->isoc_xact_pos = IFXUSB_HCSPLIT_XACTPOS_ALL; + else + ifxhc->isoc_xact_pos = IFXUSB_HCSPLIT_XACTPOS_BEGIN; + } + } + #endif + } + + ifxhc->do_ping=0; + if (_ifxhcd->core_if.snpsid < 0x4f54271a && ifxhc->speed == IFXUSB_EP_SPEED_HIGH) + ifxhc->do_ping=1; + + + /* Set the split attributes */ + ifxhc->split = 0; + if (_epqh->need_split) { + ifxhc->split = 1; + ifxhc->hub_addr = urb->dev->tt->hub->devnum; + ifxhc->port_addr = urb->dev->ttport; + } + + //ifxhc->uint16_t pkt_count_limit + + { + hcint_data_t hc_intr_mask; + uint8_t hc_num = ifxhc->hc_num; + ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[hc_num]; + + /* Clear old interrupt conditions for this host channel. */ + hc_intr_mask.d32 = 0xFFFFFFFF; + hc_intr_mask.b.reserved = 0; + ifxusb_wreg(&hc_regs->hcint, hc_intr_mask.d32); + + /* Enable channel interrupts required for this transfer. */ + hc_intr_mask.d32 = 0; + hc_intr_mask.b.chhltd = 1; + hc_intr_mask.b.ahberr = 1; + + ifxusb_wreg(&hc_regs->hcintmsk, hc_intr_mask.d32); + + /* Enable the top level host channel interrupt. */ + { + uint32_t intr_enable; + intr_enable = (1 << hc_num); + ifxusb_mreg(&_ifxhcd->core_if.host_global_regs->haintmsk, 0, intr_enable); + } + + /* Make sure host channel interrupts are enabled. */ + { + gint_data_t gintmsk ={.d32 = 0}; + gintmsk.b.hcintr = 1; + ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, 0, gintmsk.d32); + } + + /* + * Program the HCCHARn register with the endpoint characteristics for + * the current transfer. + */ + { + hcchar_data_t hcchar; + + hcchar.d32 = 0; + hcchar.b.devaddr = ifxhc->dev_addr; + hcchar.b.epnum = ifxhc->ep_num; + hcchar.b.lspddev = (ifxhc->speed == IFXUSB_EP_SPEED_LOW); + hcchar.b.eptype = ifxhc->ep_type; + hcchar.b.mps = ifxhc->mps; + ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); + + IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, ifxhc->hc_num); + IFX_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n" , hcchar.b.devaddr); + IFX_DEBUGPL(DBG_HCDV, " Ep Num: %d\n" , hcchar.b.epnum); + IFX_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); + IFX_DEBUGPL(DBG_HCDV, " Ep Type: %d\n" , hcchar.b.eptype); + IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , hcchar.b.mps); + IFX_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n" , hcchar.b.multicnt); + } + /* Program the HCSPLIT register for SPLITs */ + { + hcsplt_data_t hcsplt; + + hcsplt.d32 = 0; + if (ifxhc->split) + { + IFX_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", ifxhc->hc_num, + (ifxhc->split==2) ? "CSPLIT" : "SSPLIT"); + hcsplt.b.spltena = 1; + hcsplt.b.compsplt = (ifxhc->split==2); + #ifdef __EN_ISOC__ + if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) + hcsplt.b.xactpos = ifxhc->isoc_xact_pos; + else + #endif + hcsplt.b.xactpos = IFXUSB_HCSPLIT_XACTPOS_ALL; + hcsplt.b.hubaddr = ifxhc->hub_addr; + hcsplt.b.prtaddr = ifxhc->port_addr; + IFX_DEBUGPL(DBG_HCDV, " comp split %d\n" , hcsplt.b.compsplt); + IFX_DEBUGPL(DBG_HCDV, " xact pos %d\n" , hcsplt.b.xactpos); + IFX_DEBUGPL(DBG_HCDV, " hub addr %d\n" , hcsplt.b.hubaddr); + IFX_DEBUGPL(DBG_HCDV, " port addr %d\n" , hcsplt.b.prtaddr); + IFX_DEBUGPL(DBG_HCDV, " is_in %d\n" , ifxhc->is_in); + IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , ifxhc->mps); + IFX_DEBUGPL(DBG_HCDV, " xferlen: %d\n" , ifxhc->xfer_len); + } + ifxusb_wreg(&hc_regs->hcsplt, hcsplt.d32); + } + } + + ifxhc->nak_retry_r=ifxhc->nak_retry=0; + ifxhc->nak_countdown_r=ifxhc->nak_countdown=0; + + if (ifxhc->split) + { + if(ifxhc->is_in) + { + } + else + { + } + } + else if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) + { + if(ifxhc->is_in) + { + } + else + { + } + } + else if(_epqh->ep_type==IFXUSB_EP_TYPE_BULK) + { + if(ifxhc->is_in) + { +// ifxhc->nak_retry_r=ifxhc->nak_retry=nak_retry_max; +// ifxhc->nak_countdown_r=ifxhc->nak_countdown=nak_countdown_max; + } + else + { + } + } + else if(_epqh->ep_type==IFXUSB_EP_TYPE_INTR) + { + if(ifxhc->is_in) + { + } + else + { + } + } + else if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) + { + if(ifxhc->is_in) + { + } + else + { + } + } + + return 1; +} + +/*! + \brief This function selects transactions from the HCD transfer schedule and + assigns them to available host channels. It is called from HCD interrupt + handler functions. + */ +static void select_eps_sub(ifxhcd_hcd_t *_ifxhcd) +{ + struct list_head *epqh_ptr; + struct list_head *urbd_ptr; + ifxhcd_epqh_t *epqh; + ifxhcd_urbd_t *urbd; + int ret_val=0; + + /*== AVM/BC 20101111 Function called with Lock ==*/ + +// #ifdef __DEBUG__ +// IFX_DEBUGPL(DBG_HCD, " ifxhcd_select_ep\n"); +// #endif + + /* Process entries in the periodic ready list. */ + #ifdef __EN_ISOC__ + epqh_ptr = _ifxhcd->epqh_isoc_ready.next; + while (epqh_ptr != &_ifxhcd->epqh_isoc_ready && !list_empty(&_ifxhcd->free_hc_list)) + { + epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); + epqh_ptr = epqh_ptr->next; + if(epqh->period_do) + { + if(assign_and_init_hc(_ifxhcd, epqh)) + { + IFX_DEBUGPL(DBG_HCD, " select_eps ISOC\n"); + list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_active); + epqh->is_active=1; + ret_val=1; + epqh->period_do=0; + } + } + } + #endif + + epqh_ptr = _ifxhcd->epqh_intr_ready.next; + while (epqh_ptr != &_ifxhcd->epqh_intr_ready && !list_empty(&_ifxhcd->free_hc_list)) + { + epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); + epqh_ptr = epqh_ptr->next; + if(epqh->period_do) + { + if(assign_and_init_hc(_ifxhcd, epqh)) + { + IFX_DEBUGPL(DBG_HCD, " select_eps INTR\n"); + list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_intr_active); + epqh->is_active=1; + ret_val=1; + epqh->period_do=0; + } + } + } + + epqh_ptr = _ifxhcd->epqh_np_ready.next; + while (epqh_ptr != &_ifxhcd->epqh_np_ready && !list_empty(&_ifxhcd->free_hc_list)) // may need to preserve at lease one for period + { + epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); + epqh_ptr = epqh_ptr->next; + if(assign_and_init_hc(_ifxhcd, epqh)) + { + IFX_DEBUGPL(DBG_HCD, " select_eps CTRL/BULK\n"); + list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_np_active); + epqh->is_active=1; + ret_val=1; + } + } + if(ret_val) + /*== AVM/BC 20101111 Function called with Lock ==*/ + process_channels_sub(_ifxhcd); + + /* AVM/BC 20101111 Urbds completion loop */ + while (!list_empty(&_ifxhcd->urbd_complete_list)) + { + urbd_ptr = _ifxhcd->urbd_complete_list.next; + list_del_init(urbd_ptr); + + urbd = list_entry(urbd_ptr, ifxhcd_urbd_t, urbd_list_entry); + + ifxhcd_complete_urb(_ifxhcd, urbd, urbd->status); + + } + +} + +static void select_eps_func(unsigned long data) +{ + unsigned long flags; + + ifxhcd_hcd_t *ifxhcd; + ifxhcd=((ifxhcd_hcd_t *)data); + + /* AVM/BC 20101111 select_eps_in_use flag removed */ + + SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); + + /*if(ifxhcd->select_eps_in_use){ + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); + return; + } + ifxhcd->select_eps_in_use=1; + */ + + select_eps_sub(ifxhcd); + + //ifxhcd->select_eps_in_use=0; + + SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); +} + +void select_eps(ifxhcd_hcd_t *_ifxhcd) +{ + if(in_irq()) + { + if(!_ifxhcd->select_eps.func) + { + _ifxhcd->select_eps.next = NULL; + _ifxhcd->select_eps.state = 0; + atomic_set( &_ifxhcd->select_eps.count, 0); + _ifxhcd->select_eps.func = select_eps_func; + _ifxhcd->select_eps.data = (unsigned long)_ifxhcd; + } + tasklet_schedule(&_ifxhcd->select_eps); + } + else + { + unsigned long flags; + + /* AVM/BC 20101111 select_eps_in_use flag removed */ + + SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); + + /*if(_ifxhcd->select_eps_in_use){ + printk ("select_eps non_irq: busy\n"); + SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); + return; + } + _ifxhcd->select_eps_in_use=1; + */ + + select_eps_sub(_ifxhcd); + + //_ifxhcd->select_eps_in_use=0; + + SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); + } +} + +/*! + \brief + */ +static void process_unaligned( ifxhcd_epqh_t *_epqh) +{ + #if defined(__UNALIGNED_BUFFER_ADJ__) + if(!_epqh->aligned_checked) + { + uint32_t xfer_len; + xfer_len=_epqh->urbd->xfer_len; + if(_epqh->urbd->is_in && xfer_len<_epqh->mps) + xfer_len = _epqh->mps; + _epqh->using_aligned_buf=0; + + if(xfer_len > 0 && ((unsigned long)_epqh->urbd->xfer_buff) & 3) + { + if( _epqh->aligned_buf + && _epqh->aligned_buf_len > 0 + && _epqh->aligned_buf_len < xfer_len + ) + { + ifxusb_free_buf(_epqh->aligned_buf); + _epqh->aligned_buf=NULL; + _epqh->aligned_buf_len=0; + } + if(! _epqh->aligned_buf || ! _epqh->aligned_buf_len) + { + _epqh->aligned_buf = ifxusb_alloc_buf(xfer_len, _epqh->urbd->is_in); + if(_epqh->aligned_buf) + _epqh->aligned_buf_len = xfer_len; + } + if(_epqh->aligned_buf) + { + if(!_epqh->urbd->is_in) + memcpy(_epqh->aligned_buf, _epqh->urbd->xfer_buff, xfer_len); + _epqh->using_aligned_buf=1; + _epqh->hc->xfer_buff = _epqh->aligned_buf; + } + else + IFX_WARN("%s():%d\n",__func__,__LINE__); + } + if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) + { + _epqh->using_aligned_setup=0; + if(((unsigned long)_epqh->urbd->setup_buff) & 3) + { + if(! _epqh->aligned_setup) + _epqh->aligned_setup = ifxusb_alloc_buf(8,0); + if(_epqh->aligned_setup) + { + memcpy(_epqh->aligned_setup, _epqh->urbd->setup_buff, 8); + _epqh->using_aligned_setup=1; + } + else + IFX_WARN("%s():%d\n",__func__,__LINE__); + _epqh->hc->xfer_buff = _epqh->aligned_setup; + } + } + } + #elif defined(__UNALIGNED_BUFFER_CHK__) + if(!_epqh->aligned_checked) + { + if(_epqh->urbd->is_in) + { + if(_epqh->urbd->xfer_len==0) + IFX_WARN("%s():%d IN xfer while length is zero \n",__func__,__LINE__); + else{ + if(_epqh->urbd->xfer_len < _epqh->mps) + IFX_WARN("%s():%d IN xfer while length < mps \n",__func__,__LINE__); + + if(((unsigned long)_epqh->urbd->xfer_buff) & 3) + IFX_WARN("%s():%d IN xfer Buffer UNALIGNED\n",__func__,__LINE__); + } + } + else + { + if(_epqh->urbd->xfer_len > 0 && (((unsigned long)_epqh->urbd->xfer_buff) & 3) ) + IFX_WARN("%s():%d OUT xfer Buffer UNALIGNED\n",__func__,__LINE__); + } + + if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) + { + if(((unsigned long)_epqh->urbd->setup_buff) & 3) + IFX_WARN("%s():%d SETUP xfer Buffer UNALIGNED\n",__func__,__LINE__); + } + } + #endif + _epqh->aligned_checked=1; +} + + +/*! + \brief + */ +void process_channels_sub(ifxhcd_hcd_t *_ifxhcd) +{ + ifxhcd_epqh_t *epqh; + struct list_head *epqh_item; + struct ifxhcd_hc *hc; + + #ifdef __EN_ISOC__ + if (!list_empty(&_ifxhcd->epqh_isoc_active)) + { + for (epqh_item = _ifxhcd->epqh_isoc_active.next; + epqh_item != &_ifxhcd->epqh_isoc_active; + ) + { + epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); + epqh_item = epqh_item->next; + hc=epqh->hc; + if(hc && !hc->xfer_started && epqh->period_do) + { + if(hc->split==0 + || hc->split==1 + ) + { + //epqh->ping_state = 0; + process_unaligned(epqh); + hc->wait_for_sof=epqh->wait_for_sof; + epqh->wait_for_sof=0; + ifxhcd_hc_start(&_ifxhcd->core_if, hc); + epqh->period_do=0; + { + gint_data_t gintsts = {.d32 = 0}; + gintsts.b.sofintr = 1; + ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk,0, gintsts.d32); + } + } + } + } + } + #endif + + if (!list_empty(&_ifxhcd->epqh_intr_active)) + { + for (epqh_item = _ifxhcd->epqh_intr_active.next; + epqh_item != &_ifxhcd->epqh_intr_active; + ) + { + epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); + epqh_item = epqh_item->next; + hc=epqh->hc; + if(hc && !hc->xfer_started && epqh->period_do) + { + if(hc->split==0 + || hc->split==1 + ) + { + //epqh->ping_state = 0; + process_unaligned(epqh); + hc->wait_for_sof=epqh->wait_for_sof; + epqh->wait_for_sof=0; + ifxhcd_hc_start(&_ifxhcd->core_if, hc); + epqh->period_do=0; +#ifdef __USE_TIMER_4_SOF__ + /* AVM/WK change: let hc_start decide, if irq is needed */ +#else + { + gint_data_t gintsts = {.d32 = 0}; + gintsts.b.sofintr = 1; + ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk,0, gintsts.d32); + } +#endif + } + } + + } + } + + if (!list_empty(&_ifxhcd->epqh_np_active)) + { + for (epqh_item = _ifxhcd->epqh_np_active.next; + epqh_item != &_ifxhcd->epqh_np_active; + ) + { + epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); + epqh_item = epqh_item->next; + hc=epqh->hc; + if(hc) + { + if(!hc->xfer_started) + { + if(hc->split==0 + || hc->split==1 + //|| hc->split_counter == 0 + ) + { + //epqh->ping_state = 0; + process_unaligned(epqh); + hc->wait_for_sof=epqh->wait_for_sof; + epqh->wait_for_sof=0; + ifxhcd_hc_start(&_ifxhcd->core_if, hc); + } + } + } + } + } +} + +void process_channels(ifxhcd_hcd_t *_ifxhcd) +{ + unsigned long flags; + + /* AVM/WK Fix: use spin_lock instead busy flag + **/ + SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); + + //if(_ifxhcd->process_channels_in_use) + // return; + //_ifxhcd->process_channels_in_use=1; + + process_channels_sub(_ifxhcd); + //_ifxhcd->process_channels_in_use=0; + SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); +} + + +#ifdef __HC_XFER_TIMEOUT__ + static void hc_xfer_timeout(unsigned long _ptr) + { + hc_xfer_info_t *xfer_info = (hc_xfer_info_t *)_ptr; + int hc_num = xfer_info->hc->hc_num; + IFX_WARN("%s: timeout on channel %d\n", __func__, hc_num); + IFX_WARN(" start_hcchar_val 0x%08x\n", xfer_info->hc->start_hcchar_val); + } +#endif + +void ifxhcd_hc_dumb_rx(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc,uint8_t *dump_buf) +{ + ifxusb_hc_regs_t *hc_regs = _core_if->hc_regs[_ifxhc->hc_num]; + hctsiz_data_t hctsiz= { .d32=0 }; + hcchar_data_t hcchar; + + + _ifxhc->xfer_len = _ifxhc->mps; + hctsiz.b.xfersize = _ifxhc->mps; + hctsiz.b.pktcnt = 0; + hctsiz.b.pid = _ifxhc->data_pid_start; + ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); + + ifxusb_wreg(&hc_regs->hcdma, (uint32_t)(CPHYSADDR( ((uint32_t)(dump_buf))))); + + { + hcint_data_t hcint= { .d32=0 }; +// hcint.b.nak =1; +// hcint.b.nyet=1; +// hcint.b.ack =1; + hcint.d32 =0xFFFFFFFF; + ifxusb_wreg(&hc_regs->hcint, hcint.d32); + } + + /* Set host channel enable after all other setup is complete. */ + hcchar.b.chen = 1; + hcchar.b.chdis = 0; + hcchar.b.epdir = 1; + IFX_DEBUGPL(DBG_HCDV, " HCCHART: 0x%08x\n", hcchar.d32); + ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); +} + +/*! + \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(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc) +{ + hctsiz_data_t hctsiz= { .d32=0 }; + hcchar_data_t hcchar; + 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; + + hctsiz.b.dopng = 0; +// if(_ifxhc->do_ping && !_ifxhc->is_in) hctsiz.b.dopng = 1; + + _ifxhc->nak_countdown=_ifxhc->nak_countdown_r; + + /* AVM/BC 20101111 Workaround: Always PING if HI-Speed Out and xfer_len > 0 */ + if(/*_ifxhc->do_ping &&*/ + (!_ifxhc->is_in) && + (_ifxhc->speed == IFXUSB_EP_SPEED_HIGH) && + ((_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK) || ((_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL) && (_ifxhc->control_phase != IFXHCD_CONTROL_SETUP))) && + _ifxhc->xfer_len + ) + hctsiz.b.dopng = 1; + + _ifxhc->xfer_started = 1; + + if(_ifxhc->epqh->pkt_count_limit > 0 && _ifxhc->epqh->pkt_count_limit < max_hc_pkt_count ) + { + max_hc_pkt_count=_ifxhc->epqh->pkt_count_limit; + if(max_hc_pkt_count * _ifxhc->mps < max_hc_xfer_size) + max_hc_xfer_size = max_hc_pkt_count * _ifxhc->mps; + } + if (_ifxhc->split > 0) + { + { + gint_data_t gintsts = {.d32 = 0}; + gintsts.b.sofintr = 1; + ifxusb_mreg(&_core_if->core_global_regs->gintmsk,0, gintsts.d32); + } + + _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) + { + /*== AVM/BC WK 20110421 ZERO PACKET Workaround: Is not an error ==*/ + //if(_ifxhc->short_rw==0) + // printk(KERN_INFO "%s() line %d: ZLP write without short_rw set!\n",__func__,__LINE__); + _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; + /*== AVM/BC WK 20110421 ZERO PACKET Workaround / check if short_rw is needed ==*/ + if(_ifxhc->start_pkt_count * _ifxhc->mps != _ifxhc->xfer_len ) + _ifxhc->short_rw = 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 + + 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, "%s: Channel %d\n", __func__, _ifxhc->hc_num); + 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; + + #ifdef __EN_ISOC__ + 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.d32 = ifxusb_rreg(&hc_regs->hcchar); +// hcchar.b.multicnt = _ifxhc->multi_count; + hcchar.b.multicnt = 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); + + /* == 20110901 AVM/WK Fix: Clear IRQ flags in any case ==*/ + { + hcint_data_t hcint= { .d32=0 }; + hcint.d32 =0xFFFFFFFF; + ifxusb_wreg(&hc_regs->hcint, hcint.d32); + } + + if(_ifxhc->wait_for_sof==0) + { + hcint_data_t hcint; + + hcint.d32=ifxusb_rreg(&hc_regs->hcintmsk); + + hcint.b.nak =0; + hcint.b.ack =0; + /* == 20110901 AVM/WK Fix: We don't need NOT YET IRQ ==*/ + hcint.b.nyet=0; + if(_ifxhc->nak_countdown_r) + hcint.b.nak =1; + ifxusb_wreg(&hc_regs->hcintmsk, hcint.d32); + + /* AVM WK / BC 20100827 + * MOVED. Oddframe updated inmediatly before write HCChar Register. + */ + 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; + _ifxhc->hcchar=hcchar.d32; + } + + ifxusb_wreg(&hc_regs->hcchar, _ifxhc->hcchar); +#ifdef __USE_TIMER_4_SOF__ + } else { + //activate SOF IRQ + gint_data_t gintsts = {.d32 = 0}; + gintsts.b.sofintr = 1; + ifxusb_mreg(&_core_if->core_global_regs->gintmsk,0, gintsts.d32); +#endif + } + + #ifdef __HC_XFER_TIMEOUT__ + /* Start a timer for this transfer. */ + init_timer(&_ifxhc->hc_xfer_timer); + _ifxhc->hc_xfer_timer.function = hc_xfer_timeout; + _ifxhc->hc_xfer_timer.core_if = _core_if; + _ifxhc->hc_xfer_timer.hc = _ifxhc; + _ifxhc->hc_xfer_timer.data = (unsigned long)(&_ifxhc->hc_xfer_info); + _ifxhc->hc_xfer_timer.expires = jiffies + (HZ*10); + add_timer(&_ifxhc->hc_xfer_timer); + #endif +} + +/*! + \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. + */ +void 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); + + 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. + */ + return; + } + } + + if (_ifxhc->halting) + { + /* + * 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, _hc->halting already set ***\n", + __func__, _ifxhc->hc_num); + #endif + //ifxusb_dump_global_registers(_core_if); */ + //ifxusb_dump_host_registers(_core_if); */ + return; + } + hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); + /* == AVM/WK 20100709 halt channel only if enabled ==*/ + if (hcchar.b.chen) { + _ifxhc->halting = 1; + hcchar.b.chdis = 1; + + ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); + _ifxhc->halt_status = _halt_status; + } + + 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, " halting: %d\n" , _ifxhc->halting); + IFX_DEBUGPL(DBG_HCDV, " halt_status: %d\n" , _ifxhc->halt_status); + + return; +} + +/*! + \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->xfer_started = 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 __HC_XFER_TIMEOUT__ + del_timer(&_ifxhc->hc_xfer_timer); + #endif + #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); + } + } + } + + 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(" xfer_started: %d\n" , hc->xfer_started); + 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 Active:\n"); + list_for_each(item, &_ifxhcd->epqh_np_active) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + IFX_PRINT(" NP Ready:\n"); + list_for_each(item, &_ifxhcd->epqh_np_ready) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + IFX_PRINT(" INTR Active:\n"); + list_for_each(item, &_ifxhcd->epqh_intr_active) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + IFX_PRINT(" INTR Ready:\n"); + list_for_each(item, &_ifxhcd->epqh_intr_ready) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + #ifdef __EN_ISOC__ + IFX_PRINT(" ISOC Active:\n"); + list_for_each(item, &_ifxhcd->epqh_isoc_active) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + IFX_PRINT(" ISOC Ready:\n"); + list_for_each(item, &_ifxhcd->epqh_isoc_ready) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + #endif + IFX_PRINT(" Standby:\n"); + list_for_each(item, &_ifxhcd->epqh_stdby) + { + epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); + IFX_PRINT(" %p\n", epqh_item); + } + } + } +#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_started: %d\n" , hc->xfer_started); + 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(" halting: %d\n" , hc->halting); + 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); + #ifdef __EN_ISOC__ + 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(" do_ping: %d\n" , hc->do_ping); + IFX_PRINT(" control_phase: %d\n" , hc->control_phase); + IFX_PRINT(" pkt_count_limit: %d\n", hc->epqh->pkt_count_limit); + IFX_PRINT(" start_pkt_count: %d\n" , hc->start_pkt_count); + } + IFX_PRINT("************************************************************\n"); + IFX_PRINT("\n"); + } +#endif //__ENABLE_DUMP__ +