diff options
Diffstat (limited to 'target/linux/lantiq/files-3.3/drivers/usb/dwc_otg/dwc_otg_hcd.c')
-rw-r--r-- | target/linux/lantiq/files-3.3/drivers/usb/dwc_otg/dwc_otg_hcd.c | 2870 |
1 files changed, 0 insertions, 2870 deletions
diff --git a/target/linux/lantiq/files-3.3/drivers/usb/dwc_otg/dwc_otg_hcd.c b/target/linux/lantiq/files-3.3/drivers/usb/dwc_otg/dwc_otg_hcd.c deleted file mode 100644 index ad6bc72ce8..0000000000 --- a/target/linux/lantiq/files-3.3/drivers/usb/dwc_otg/dwc_otg_hcd.c +++ /dev/null @@ -1,2870 +0,0 @@ -/* ========================================================================== - * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd.c $ - * $Revision: 1.1.1.1 $ - * $Date: 2009-04-17 06:15:34 $ - * $Change: 631780 $ - * - * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, - * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless - * otherwise expressly agreed to in writing between Synopsys and you. - * - * The Software IS NOT an item of Licensed Software or Licensed Product under - * any End User Software License Agreement or Agreement for Licensed Product - * with Synopsys or any supplement thereto. You are permitted to use and - * redistribute this Software in source and binary forms, with or without - * modification, provided that redistributions of source code must retain this - * notice. You may not view, use, disclose, copy or distribute this file or - * any information contained herein except pursuant to this license grant from - * Synopsys. If you do not agree with this notice, including the disclaimer - * below, then you are not authorized to use the Software. - * - * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * ========================================================================== */ -#ifndef DWC_DEVICE_ONLY - -/** - * @file - * - * This file contains the implementation of the HCD. In Linux, the HCD - * implements the hc_driver API. - */ -#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 "dwc_otg_driver.h" -#include "dwc_otg_hcd.h" -#include "dwc_otg_regs.h" - -#include <asm/irq.h> -#include "dwc_otg_ifx.h" // for Infineon platform specific. -extern atomic_t release_later; - -static u64 dma_mask = DMA_BIT_MASK(32); - -static const char dwc_otg_hcd_name [] = "dwc_otg_hcd"; -static const struct hc_driver dwc_otg_hc_driver = -{ - .description = dwc_otg_hcd_name, - .product_desc = "DWC OTG Controller", - .hcd_priv_size = sizeof(dwc_otg_hcd_t), - .irq = dwc_otg_hcd_irq, - .flags = HCD_MEMORY | HCD_USB2, - //.reset = - .start = dwc_otg_hcd_start, - //.suspend = - //.resume = - .stop = dwc_otg_hcd_stop, - .urb_enqueue = dwc_otg_hcd_urb_enqueue, - .urb_dequeue = dwc_otg_hcd_urb_dequeue, - .endpoint_disable = dwc_otg_hcd_endpoint_disable, - .get_frame_number = dwc_otg_hcd_get_frame_number, - .hub_status_data = dwc_otg_hcd_hub_status_data, - .hub_control = dwc_otg_hcd_hub_control, - //.hub_suspend = - //.hub_resume = -}; - - -/** - * Work queue function for starting the HCD when A-Cable is connected. - * The dwc_otg_hcd_start() must be called in a process context. - */ -static void hcd_start_func(struct work_struct *work) -{ - struct dwc_otg_hcd *priv = - container_of(work, struct dwc_otg_hcd, start_work); - struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p; - DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, usb_hcd); - if (usb_hcd) { - dwc_otg_hcd_start(usb_hcd); - } -} - - -/** - * HCD Callback function for starting the HCD when A-Cable is - * connected. - * - * @param _p void pointer to the <code>struct usb_hcd</code> - */ -static int32_t dwc_otg_hcd_start_cb(void *_p) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_p); - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - hprt0_data_t hprt0; - if (core_if->op_state == B_HOST) { - /* - * Reset the port. During a HNP mode switch the reset - * needs to occur within 1ms and have a duration of at - * least 50ms. - */ - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtrst = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - ((struct usb_hcd *)_p)->self.is_b_host = 1; - } else { - ((struct usb_hcd *)_p)->self.is_b_host = 0; - } - /* Need to start the HCD in a non-interrupt context. */ - INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func); - dwc_otg_hcd->_p = _p; - schedule_work(&dwc_otg_hcd->start_work); - return 1; -} - - -/** - * HCD Callback function for stopping the HCD. - * - * @param _p void pointer to the <code>struct usb_hcd</code> - */ -static int32_t dwc_otg_hcd_stop_cb( void *_p ) -{ - struct usb_hcd *usb_hcd = (struct usb_hcd *)_p; - DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); - dwc_otg_hcd_stop( usb_hcd ); - return 1; -} -static void del_xfer_timers(dwc_otg_hcd_t *_hcd) -{ -#ifdef DEBUG - int i; - int num_channels = _hcd->core_if->core_params->host_channels; - for (i = 0; i < num_channels; i++) { - del_timer(&_hcd->core_if->hc_xfer_timer[i]); - } -#endif /* */ -} - -static void del_timers(dwc_otg_hcd_t *_hcd) -{ - del_xfer_timers(_hcd); - del_timer(&_hcd->conn_timer); -} - -/** - * Processes all the URBs in a single list of QHs. Completes them with - * -ETIMEDOUT and frees the QTD. - */ -static void kill_urbs_in_qh_list(dwc_otg_hcd_t * _hcd, - struct list_head *_qh_list) -{ - struct list_head *qh_item; - dwc_otg_qh_t *qh; - struct list_head *qtd_item; - dwc_otg_qtd_t *qtd; - - list_for_each(qh_item, _qh_list) { - qh = list_entry(qh_item, dwc_otg_qh_t, qh_list_entry); - for (qtd_item = qh->qtd_list.next; qtd_item != &qh->qtd_list; - qtd_item = qh->qtd_list.next) { - qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry); - if (qtd->urb != NULL) { - dwc_otg_hcd_complete_urb(_hcd, qtd->urb,-ETIMEDOUT); - } - dwc_otg_hcd_qtd_remove_and_free(qtd); - } - } -} - -/** - * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic - * and periodic schedules. The QTD associated with each URB is removed from - * the schedule and freed. This function may be called when a disconnect is - * detected or when the HCD is being stopped. - */ -static void kill_all_urbs(dwc_otg_hcd_t *_hcd) -{ - kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_deferred); - kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_inactive); - kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_active); - kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_inactive); - kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_ready); - kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_assigned); - kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_queued); -} - -/** - * HCD Callback function for disconnect of the HCD. - * - * @param _p void pointer to the <code>struct usb_hcd</code> - */ -static int32_t dwc_otg_hcd_disconnect_cb( void *_p ) -{ - gintsts_data_t intr; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_p); - - DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); - - /* - * Set status flags for the hub driver. - */ - dwc_otg_hcd->flags.b.port_connect_status_change = 1; - dwc_otg_hcd->flags.b.port_connect_status = 0; - - /* - * Shutdown any transfers in process by clearing the Tx FIFO Empty - * interrupt mask and status bits and disabling subsequent host - * channel interrupts. - */ - intr.d32 = 0; - intr.b.nptxfempty = 1; - intr.b.ptxfempty = 1; - intr.b.hcintr = 1; - dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintmsk, intr.d32, 0); - dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintsts, intr.d32, 0); - - del_timers(dwc_otg_hcd); - - /* - * Turn off the vbus power only if the core has transitioned to device - * mode. If still in host mode, need to keep power on to detect a - * reconnection. - */ - if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) { - if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) { - hprt0_data_t hprt0 = { .d32=0 }; - DWC_PRINT("Disconnect: PortPower off\n"); - hprt0.b.prtpwr = 0; - dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); - } - - dwc_otg_disable_host_interrupts( dwc_otg_hcd->core_if ); - } - - /* Respond with an error status to all URBs in the schedule. */ - kill_all_urbs(dwc_otg_hcd); - - if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) { - /* Clean up any host channels that were in use. */ - int num_channels; - int i; - dwc_hc_t *channel; - dwc_otg_hc_regs_t *hc_regs; - hcchar_data_t hcchar; - - num_channels = dwc_otg_hcd->core_if->core_params->host_channels; - - if (!dwc_otg_hcd->core_if->dma_enable) { - /* Flush out any channel requests in slave mode. */ - for (i = 0; i < num_channels; i++) { - channel = dwc_otg_hcd->hc_ptr_array[i]; - if (list_empty(&channel->hc_list_entry)) { - hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - if (hcchar.b.chen) { - hcchar.b.chen = 0; - hcchar.b.chdis = 1; - hcchar.b.epdir = 0; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - } - } - } - } - - for (i = 0; i < num_channels; i++) { - channel = dwc_otg_hcd->hc_ptr_array[i]; - if (list_empty(&channel->hc_list_entry)) { - hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - if (hcchar.b.chen) { - /* Halt the channel. */ - hcchar.b.chdis = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - } - - dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, channel); - list_add_tail(&channel->hc_list_entry, - &dwc_otg_hcd->free_hc_list); - } - } - } - - /* A disconnect will end the session so the B-Device is no - * longer a B-host. */ - ((struct usb_hcd *)_p)->self.is_b_host = 0; - - return 1; -} - -/** - * Connection timeout function. An OTG host is required to display a - * message if the device does not connect within 10 seconds. - */ -void dwc_otg_hcd_connect_timeout( unsigned long _ptr ) -{ - DWC_DEBUGPL(DBG_HCDV, "%s(%x)\n", __func__, (int)_ptr); - DWC_PRINT( "Connect Timeout\n"); - DWC_ERROR( "Device Not Connected/Responding\n" ); -} - -/** - * Start the connection timer. An OTG host is required to display a - * message if the device does not connect within 10 seconds. The - * timer is deleted if a port connect interrupt occurs before the - * timer expires. - */ -static void dwc_otg_hcd_start_connect_timer( dwc_otg_hcd_t *_hcd) -{ - init_timer( &_hcd->conn_timer ); - _hcd->conn_timer.function = dwc_otg_hcd_connect_timeout; - _hcd->conn_timer.data = (unsigned long)0; - _hcd->conn_timer.expires = jiffies + (HZ*10); - add_timer( &_hcd->conn_timer ); -} - -/** - * HCD Callback function for disconnect of the HCD. - * - * @param _p void pointer to the <code>struct usb_hcd</code> - */ -static int32_t dwc_otg_hcd_session_start_cb( void *_p ) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_p); - DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); - dwc_otg_hcd_start_connect_timer( dwc_otg_hcd ); - return 1; -} - -/** - * HCD Callback structure for handling mode switching. - */ -static dwc_otg_cil_callbacks_t hcd_cil_callbacks = { - .start = dwc_otg_hcd_start_cb, - .stop = dwc_otg_hcd_stop_cb, - .disconnect = dwc_otg_hcd_disconnect_cb, - .session_start = dwc_otg_hcd_session_start_cb, - .p = 0, -}; - - -/** - * Reset tasklet function - */ -static void reset_tasklet_func (unsigned long data) -{ - dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t*)data; - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - hprt0_data_t hprt0; - - DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n"); - - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtrst = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - mdelay (60); - - hprt0.b.prtrst = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - dwc_otg_hcd->flags.b.port_reset_change = 1; - - return; -} - -static struct tasklet_struct reset_tasklet = { - .next = NULL, - .state = 0, - .count = ATOMIC_INIT(0), - .func = reset_tasklet_func, - .data = 0, -}; - -/** - * Initializes the HCD. This function allocates memory for and initializes the - * static parts of the usb_hcd and dwc_otg_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 init_hcd_usecs(dwc_otg_hcd_t *_hcd); - -int __devinit dwc_otg_hcd_init(struct device *_dev, dwc_otg_device_t * dwc_otg_device) -{ - struct usb_hcd *hcd = NULL; - dwc_otg_hcd_t *dwc_otg_hcd = NULL; - dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); - - int num_channels; - int i; - dwc_hc_t *channel; - - int retval = 0; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n"); - - /* - * Allocate memory for the base HCD plus the DWC OTG HCD. - * Initialize the base HCD. - */ - hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dev_name(_dev)); - if (hcd == NULL) { - retval = -ENOMEM; - goto error1; - } - dev_set_drvdata(_dev, dwc_otg_device); /* fscz restore */ - hcd->regs = otg_dev->base; - hcd->rsrc_start = (int)otg_dev->base; - - hcd->self.otg_port = 1; - - /* Initialize the DWC OTG HCD. */ - dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); - dwc_otg_hcd->core_if = otg_dev->core_if; - otg_dev->hcd = dwc_otg_hcd; - - /* Register the HCD CIL Callbacks */ - dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, - &hcd_cil_callbacks, hcd); - - /* Initialize the non-periodic schedule. */ - INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_inactive); - INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_active); - INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_deferred); - - /* Initialize the periodic schedule. */ - INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_inactive); - INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_ready); - INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_assigned); - INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_queued); - - /* - * Create a host channel descriptor for each host channel implemented - * in the controller. Initialize the channel descriptor array. - */ - INIT_LIST_HEAD(&dwc_otg_hcd->free_hc_list); - num_channels = dwc_otg_hcd->core_if->core_params->host_channels; - for (i = 0; i < num_channels; i++) { - channel = kmalloc(sizeof(dwc_hc_t), GFP_KERNEL); - if (channel == NULL) { - retval = -ENOMEM; - DWC_ERROR("%s: host channel allocation failed\n", __func__); - goto error2; - } - memset(channel, 0, sizeof(dwc_hc_t)); - channel->hc_num = i; - dwc_otg_hcd->hc_ptr_array[i] = channel; -#ifdef DEBUG - init_timer(&dwc_otg_hcd->core_if->hc_xfer_timer[i]); -#endif - - DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, channel); - } - - /* Initialize the Connection timeout timer. */ - init_timer( &dwc_otg_hcd->conn_timer ); - - /* Initialize reset tasklet. */ - reset_tasklet.data = (unsigned long) dwc_otg_hcd; - dwc_otg_hcd->reset_tasklet = &reset_tasklet; - - /* Set device flags indicating whether the HCD supports DMA. */ - if (otg_dev->core_if->dma_enable) { - DWC_PRINT("Using DMA mode\n"); - //_dev->dma_mask = (void *)~0; - //_dev->coherent_dma_mask = ~0; - _dev->dma_mask = &dma_mask; - _dev->coherent_dma_mask = DMA_BIT_MASK(32); - } else { - DWC_PRINT("Using Slave mode\n"); - _dev->dma_mask = (void *)0; - _dev->coherent_dma_mask = 0; - } - - init_hcd_usecs(dwc_otg_hcd); - /* - * Finish generic HCD initialization and start the HCD. This function - * allocates the DMA buffer pool, registers the USB bus, requests the - * IRQ line, and calls dwc_otg_hcd_start method. - */ - retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED); - if (retval < 0) { - goto error2; - } - - /* - * Allocate space for storing data on status transactions. Normally no - * data is sent, but this space acts as a bit bucket. This must be - * done after usb_add_hcd since that function allocates the DMA buffer - * pool. - */ - if (otg_dev->core_if->dma_enable) { - dwc_otg_hcd->status_buf = - dma_alloc_coherent(_dev, - DWC_OTG_HCD_STATUS_BUF_SIZE, - &dwc_otg_hcd->status_buf_dma, - GFP_KERNEL | GFP_DMA); - } else { - dwc_otg_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE, - GFP_KERNEL); - } - if (dwc_otg_hcd->status_buf == NULL) { - retval = -ENOMEM; - DWC_ERROR("%s: status_buf allocation failed\n", __func__); - goto error3; - } - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", - dev_name(_dev), hcd->self.busnum); - - return 0; - - /* Error conditions */ -error3: - usb_remove_hcd(hcd); -error2: - dwc_otg_hcd_free(hcd); - usb_put_hcd(hcd); -error1: - return retval; -} - -/** - * Removes the HCD. - * Frees memory and resources associated with the HCD and deregisters the bus. - */ -void dwc_otg_hcd_remove(struct device *_dev) -{ - dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); - dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; - struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n"); - - /* Turn off all interrupts */ - dwc_write_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0); - dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gahbcfg, 1, 0); - - usb_remove_hcd(hcd); - - dwc_otg_hcd_free(hcd); - - usb_put_hcd(hcd); - - return; -} - - -/* ========================================================================= - * Linux HC Driver Functions - * ========================================================================= */ - -/** - * Initializes dynamic portions of the DWC_otg HCD state. - */ -static void hcd_reinit(dwc_otg_hcd_t *_hcd) -{ - struct list_head *item; - int num_channels; - int i; - dwc_hc_t *channel; - - _hcd->flags.d32 = 0; - - _hcd->non_periodic_qh_ptr = &_hcd->non_periodic_sched_active; - _hcd->available_host_channels = _hcd->core_if->core_params->host_channels; - - /* - * Put all channels in the free channel list and clean up channel - * states. - */ - item = _hcd->free_hc_list.next; - while (item != &_hcd->free_hc_list) { - list_del(item); - item = _hcd->free_hc_list.next; - } - num_channels = _hcd->core_if->core_params->host_channels; - for (i = 0; i < num_channels; i++) { - channel = _hcd->hc_ptr_array[i]; - list_add_tail(&channel->hc_list_entry, &_hcd->free_hc_list); - dwc_otg_hc_cleanup(_hcd->core_if, channel); - } - - /* Initialize the DWC core for host mode operation. */ - dwc_otg_core_host_init(_hcd->core_if); -} - -/** Initializes the DWC_otg controller and its root hub and prepares it for host - * mode operation. Activates the root port. Returns 0 on success and a negative - * error code on failure. */ -int dwc_otg_hcd_start(struct usb_hcd *_hcd) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - dwc_otg_core_if_t * core_if = dwc_otg_hcd->core_if; - struct usb_bus *bus; - - // int retval; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); - - bus = hcd_to_bus(_hcd); - - /* Initialize the bus state. If the core is in Device Mode - * HALT the USB bus and return. */ - if (dwc_otg_is_device_mode (core_if)) { - _hcd->state = HC_STATE_HALT; - return 0; - } - _hcd->state = HC_STATE_RUNNING; - - /* Initialize and connect root hub if one is not already attached */ - if (bus->root_hub) { - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); - /* Inform the HUB driver to resume. */ - usb_hcd_resume_root_hub(_hcd); - } - else { -#if 0 - struct usb_device *udev; - udev = usb_alloc_dev(NULL, bus, 0); - if (!udev) { - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error udev alloc\n"); - return -ENODEV; - } - udev->speed = USB_SPEED_HIGH; - /* Not needed - VJ - if ((retval = usb_hcd_register_root_hub(udev, _hcd)) != 0) { - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error registering %d\n", retval); - return -ENODEV; - } - */ -#else - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error udev alloc\n"); -#endif - } - - hcd_reinit(dwc_otg_hcd); - - return 0; -} - -static void qh_list_free(dwc_otg_hcd_t *_hcd, struct list_head *_qh_list) -{ - struct list_head *item; - dwc_otg_qh_t *qh; - - if (_qh_list->next == NULL) { - /* The list hasn't been initialized yet. */ - return; - } - - /* Ensure there are no QTDs or URBs left. */ - kill_urbs_in_qh_list(_hcd, _qh_list); - - for (item = _qh_list->next; item != _qh_list; item = _qh_list->next) { - qh = list_entry(item, dwc_otg_qh_t, qh_list_entry); - dwc_otg_hcd_qh_remove_and_free(_hcd, qh); - } -} - -/** - * Halts the DWC_otg host mode operations in a clean manner. USB transfers are - * stopped. - */ -void dwc_otg_hcd_stop(struct usb_hcd *_hcd) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - hprt0_data_t hprt0 = { .d32=0 }; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); - - /* Turn off all host-specific interrupts. */ - dwc_otg_disable_host_interrupts( dwc_otg_hcd->core_if ); - - /* - * The root hub should be disconnected before this function is called. - * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) - * and the QH lists (via ..._hcd_endpoint_disable). - */ - - /* Turn off the vbus power */ - DWC_PRINT("PortPower off\n"); - hprt0.b.prtpwr = 0; - dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); - - return; -} - - -/** Returns the current frame number. */ -int dwc_otg_hcd_get_frame_number(struct usb_hcd *_hcd) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); - hfnum_data_t hfnum; - - hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if-> - host_if->host_global_regs->hfnum); - -#ifdef DEBUG_SOF - DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", hfnum.b.frnum); -#endif - return hfnum.b.frnum; -} - -/** - * Frees secondary storage associated with the dwc_otg_hcd structure contained - * in the struct usb_hcd field. - */ -void dwc_otg_hcd_free(struct usb_hcd *_hcd) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); - int i; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); - - del_timers(dwc_otg_hcd); - - /* Free memory for QH/QTD lists */ - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_deferred); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); - qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); - - /* Free memory for the host channels. */ - for (i = 0; i < MAX_EPS_CHANNELS; i++) { - dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; - if (hc != NULL) { - DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", i, hc); - kfree(hc); - } - } - - if (dwc_otg_hcd->core_if->dma_enable) { - if (dwc_otg_hcd->status_buf_dma) { - dma_free_coherent(_hcd->self.controller, - DWC_OTG_HCD_STATUS_BUF_SIZE, - dwc_otg_hcd->status_buf, - dwc_otg_hcd->status_buf_dma); - } - } else if (dwc_otg_hcd->status_buf != NULL) { - kfree(dwc_otg_hcd->status_buf); - } - - return; -} - - -#ifdef DEBUG -static void dump_urb_info(struct urb *_urb, char* _fn_name) -{ - DWC_PRINT("%s, urb %p\n", _fn_name, _urb); - DWC_PRINT(" Device address: %d\n", usb_pipedevice(_urb->pipe)); - DWC_PRINT(" Endpoint: %d, %s\n", usb_pipeendpoint(_urb->pipe), - (usb_pipein(_urb->pipe) ? "IN" : "OUT")); - DWC_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;})); - DWC_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;})); - DWC_PRINT(" Max packet size: %d\n", - usb_maxpacket(_urb->dev, _urb->pipe, usb_pipeout(_urb->pipe))); - DWC_PRINT(" Data buffer length: %d\n", _urb->transfer_buffer_length); - DWC_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n", - _urb->transfer_buffer, (void *)_urb->transfer_dma); - DWC_PRINT(" Setup buffer: %p, Setup DMA: %p\n", - _urb->setup_packet, (void *)_urb->setup_dma); - DWC_PRINT(" Interval: %d\n", _urb->interval); - if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) { - int i; - for (i = 0; i < _urb->number_of_packets; i++) { - DWC_PRINT(" ISO Desc %d:\n", i); - DWC_PRINT(" offset: %d, length %d\n", - _urb->iso_frame_desc[i].offset, - _urb->iso_frame_desc[i].length); - } - } -} - -static void dump_channel_info(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *qh) -{ - if (qh->channel != NULL) { - dwc_hc_t *hc = qh->channel; - struct list_head *item; - dwc_otg_qh_t *qh_item; - int num_channels = _hcd->core_if->core_params->host_channels; - int i; - - dwc_otg_hc_regs_t *hc_regs; - hcchar_data_t hcchar; - hcsplt_data_t hcsplt; - hctsiz_data_t hctsiz; - uint32_t hcdma; - - hc_regs = _hcd->core_if->host_if->hc_regs[hc->hc_num]; - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); - hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); - hcdma = dwc_read_reg32(&hc_regs->hcdma); - - DWC_PRINT(" Assigned to channel %p:\n", hc); - DWC_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); - DWC_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); - DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", - hc->dev_addr, hc->ep_num, hc->ep_is_in); - DWC_PRINT(" ep_type: %d\n", hc->ep_type); - DWC_PRINT(" max_packet: %d\n", hc->max_packet); - DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); - DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); - DWC_PRINT(" halt_status: %d\n", hc->halt_status); - DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); - DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); - DWC_PRINT(" qh: %p\n", hc->qh); - DWC_PRINT(" NP inactive sched:\n"); - list_for_each(item, &_hcd->non_periodic_sched_inactive) { - qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); - DWC_PRINT(" %p\n", qh_item); - } DWC_PRINT(" NP active sched:\n"); - list_for_each(item, &_hcd->non_periodic_sched_deferred) { - qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); - DWC_PRINT(" %p\n", qh_item); - } DWC_PRINT(" NP deferred sched:\n"); - list_for_each(item, &_hcd->non_periodic_sched_active) { - qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); - DWC_PRINT(" %p\n", qh_item); - } DWC_PRINT(" Channels: \n"); - for (i = 0; i < num_channels; i++) { - dwc_hc_t *hc = _hcd->hc_ptr_array[i]; - DWC_PRINT(" %2d: %p\n", i, hc); - } - } -} -#endif // DEBUG - -/** 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 dwc_otg_hcd_urb_enqueue(struct usb_hcd *_hcd, - struct urb *_urb, - gfp_t _mem_flags) -{ - unsigned long flags; - int retval; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - dwc_otg_qtd_t *qtd; - - local_irq_save(flags); - retval = usb_hcd_link_urb_to_ep(_hcd, _urb); - if (retval) { - local_irq_restore(flags); - return retval; - } -#ifdef DEBUG - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - dump_urb_info(_urb, "dwc_otg_hcd_urb_enqueue"); - } -#endif // DEBUG - if (!dwc_otg_hcd->flags.b.port_connect_status) { - /* No longer connected. */ - local_irq_restore(flags); - return -ENODEV; - } - - qtd = dwc_otg_hcd_qtd_create (_urb); - if (qtd == NULL) { - local_irq_restore(flags); - DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); - return -ENOMEM; - } - - retval = dwc_otg_hcd_qtd_add (qtd, dwc_otg_hcd); - if (retval < 0) { - DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " - "Error status %d\n", retval); - dwc_otg_hcd_qtd_free(qtd); - } - - local_irq_restore (flags); - return retval; -} - -/** Aborts/cancels a USB transfer request. Always returns 0 to indicate - * success. */ -int dwc_otg_hcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb, int _status) -{ - unsigned long flags; - dwc_otg_hcd_t *dwc_otg_hcd; - dwc_otg_qtd_t *urb_qtd; - dwc_otg_qh_t *qh; - int retval; - //struct usb_host_endpoint *_ep = NULL; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); - - local_irq_save(flags); - - retval = usb_hcd_check_unlink_urb(_hcd, _urb, _status); - if (retval) { - local_irq_restore(flags); - return retval; - } - - dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); - urb_qtd = (dwc_otg_qtd_t *)_urb->hcpriv; - if (urb_qtd == NULL) { - printk("urb_qtd is NULL for _urb %08x\n",(unsigned)_urb); - goto done; - } - qh = (dwc_otg_qh_t *) urb_qtd->qtd_qh_ptr; - if (qh == NULL) { - goto done; - } - -#ifdef DEBUG - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - dump_urb_info(_urb, "dwc_otg_hcd_urb_dequeue"); - if (urb_qtd == qh->qtd_in_process) { - dump_channel_info(dwc_otg_hcd, qh); - } - } -#endif // DEBUG - - if (urb_qtd == qh->qtd_in_process) { - /* The QTD is in process (it has been assigned to a channel). */ - - if (dwc_otg_hcd->flags.b.port_connect_status) { - /* - * If still connected (i.e. in host mode), halt the - * channel so it can be used for other transfers. If - * no longer connected, the host registers can't be - * written to halt the channel since the core is in - * device mode. - */ - dwc_otg_hc_halt(dwc_otg_hcd->core_if, qh->channel, - DWC_OTG_HC_XFER_URB_DEQUEUE); - } - } - - /* - * Free the QTD and clean up the associated QH. Leave the QH in the - * schedule if it has any remaining QTDs. - */ - dwc_otg_hcd_qtd_remove_and_free(urb_qtd); - if (urb_qtd == qh->qtd_in_process) { - dwc_otg_hcd_qh_deactivate(dwc_otg_hcd, qh, 0); - qh->channel = NULL; - qh->qtd_in_process = NULL; - } else if (list_empty(&qh->qtd_list)) { - dwc_otg_hcd_qh_remove(dwc_otg_hcd, qh); - } - -done: - local_irq_restore(flags); - _urb->hcpriv = NULL; - - /* Higher layer software sets URB status. */ - usb_hcd_unlink_urb_from_ep(_hcd, _urb); - usb_hcd_giveback_urb(_hcd, _urb, _status); - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - DWC_PRINT("Called usb_hcd_giveback_urb()\n"); - DWC_PRINT(" urb->status = %d\n", _urb->status); - } - - return 0; -} - - -/** Frees resources in the DWC_otg controller related to a given endpoint. Also - * clears state in the HCD related to the endpoint. Any URBs for the endpoint - * must already be dequeued. */ -void dwc_otg_hcd_endpoint_disable(struct usb_hcd *_hcd, - struct usb_host_endpoint *_ep) - -{ - dwc_otg_qh_t *qh; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " - "endpoint=%d\n", _ep->desc.bEndpointAddress, - dwc_ep_addr_to_endpoint(_ep->desc.bEndpointAddress)); - - qh = (dwc_otg_qh_t *)(_ep->hcpriv); - if (qh != NULL) { -#ifdef DEBUG - /** Check that the QTD list is really empty */ - if (!list_empty(&qh->qtd_list)) { - DWC_WARN("DWC OTG HCD EP DISABLE:" - " QTD List for this endpoint is not empty\n"); - } -#endif // DEBUG - - dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); - _ep->hcpriv = NULL; - } - - return; -} -extern int dwc_irq; -/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if - * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid - * interrupt. - * - * This function is called by the USB core when an interrupt occurs */ -irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *_hcd) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - - mask_and_ack_ifx_irq (dwc_irq); - return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_otg_hcd)); -} - -/** 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 dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) -{ - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - - _buf[0] = 0; - _buf[0] |= (dwc_otg_hcd->flags.b.port_connect_status_change || - dwc_otg_hcd->flags.b.port_reset_change || - dwc_otg_hcd->flags.b.port_enable_change || - dwc_otg_hcd->flags.b.port_suspend_change || - dwc_otg_hcd->flags.b.port_over_current_change) << 1; - -#ifdef DEBUG - if (_buf[0]) { - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" - " Root port status changed\n"); - DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", - dwc_otg_hcd->flags.b.port_connect_status_change); - DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", - dwc_otg_hcd->flags.b.port_reset_change); - DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", - dwc_otg_hcd->flags.b.port_enable_change); - DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", - dwc_otg_hcd->flags.b.port_suspend_change); - DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", - dwc_otg_hcd->flags.b.port_over_current_change); - } -#endif // DEBUG - return (_buf[0] != 0); -} - -#ifdef DWC_HS_ELECT_TST -/* - * Quick and dirty hack to implement the HS Electrical Test - * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. - * - * This code was copied from our userspace app "hset". It sends a - * Get Device Descriptor control sequence in two parts, first the - * Setup packet by itself, followed some time later by the In and - * Ack packets. Rather than trying to figure out how to add this - * functionality to the normal driver code, we just hijack the - * hardware, using these two function to drive the hardware - * directly. - */ - -dwc_otg_core_global_regs_t *global_regs; -dwc_otg_host_global_regs_t *hc_global_regs; -dwc_otg_hc_regs_t *hc_regs; -uint32_t *data_fifo; - -static void do_setup(void) -{ - gintsts_data_t gintsts; - hctsiz_data_t hctsiz; - hcchar_data_t hcchar; - haint_data_t haint; - hcint_data_t hcint; - - /* Enable HAINTs */ - dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); - - /* Enable HCINTs */ - dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* - * Send Setup packet (Get Device Descriptor) - */ - - /* Make sure channel is disabled */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - if (hcchar.b.chen) { - //fprintf(stderr, "Channel already enabled 1, HCCHAR = %08x\n", hcchar.d32); - hcchar.b.chdis = 1; - // hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - //sleep(1); - MDELAY(1000); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //if (hcchar.b.chen) { - // fprintf(stderr, "** Channel _still_ enabled 1, HCCHAR = %08x **\n", hcchar.d32); - //} - } - - /* Set HCTSIZ */ - hctsiz.d32 = 0; - hctsiz.b.xfersize = 8; - hctsiz.b.pktcnt = 1; - hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; - dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); - - /* Set HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; - hcchar.b.epdir = 0; - hcchar.b.epnum = 0; - hcchar.b.mps = 8; - hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - - /* Fill FIFO with Setup data for Get Device Descriptor */ - data_fifo = (uint32_t *)((char *)global_regs + 0x1000); - dwc_write_reg32(data_fifo++, 0x01000680); - dwc_write_reg32(data_fifo++, 0x00080000); - - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "Waiting for HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); - - /* Wait for host channel interrupt */ - do { - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - } while (gintsts.b.hcintr == 0); - - //fprintf(stderr, "Got HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); - - /* Disable HCINTs */ - dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); - - /* Disable HAINTs */ - dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); -} - -static void do_in_ack(void) -{ - gintsts_data_t gintsts; - hctsiz_data_t hctsiz; - hcchar_data_t hcchar; - haint_data_t haint; - hcint_data_t hcint; - host_grxsts_data_t grxsts; - - /* Enable HAINTs */ - dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); - - /* Enable HCINTs */ - dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* - * Receive Control In packet - */ - - /* Make sure channel is disabled */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - if (hcchar.b.chen) { - //fprintf(stderr, "Channel already enabled 2, HCCHAR = %08x\n", hcchar.d32); - hcchar.b.chdis = 1; - hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - //sleep(1); - MDELAY(1000); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //if (hcchar.b.chen) { - // fprintf(stderr, "** Channel _still_ enabled 2, HCCHAR = %08x **\n", hcchar.d32); - //} - } - - /* Set HCTSIZ */ - hctsiz.d32 = 0; - hctsiz.b.xfersize = 8; - hctsiz.b.pktcnt = 1; - hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; - dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); - - /* Set HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; - hcchar.b.epdir = 1; - hcchar.b.epnum = 0; - hcchar.b.mps = 8; - hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "Waiting for RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); - - /* Wait for receive status queue interrupt */ - do { - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - } while (gintsts.b.rxstsqlvl == 0); - - //fprintf(stderr, "Got RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); - - /* Read RXSTS */ - grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); - //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); - - /* Clear RXSTSQLVL in GINTSTS */ - gintsts.d32 = 0; - gintsts.b.rxstsqlvl = 1; - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - switch (grxsts.b.pktsts) { - case DWC_GRXSTS_PKTSTS_IN: - /* Read the data into the host buffer */ - if (grxsts.b.bcnt > 0) { - int i; - int word_count = (grxsts.b.bcnt + 3) / 4; - - data_fifo = (uint32_t *)((char *)global_regs + 0x1000); - - for (i = 0; i < word_count; i++) { - (void)dwc_read_reg32(data_fifo++); - } - } - - //fprintf(stderr, "Received %u bytes\n", (unsigned)grxsts.b.bcnt); - break; - - default: - //fprintf(stderr, "** Unexpected GRXSTS packet status 1 **\n"); - break; - } - - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "Waiting for RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); - - /* Wait for receive status queue interrupt */ - do { - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - } while (gintsts.b.rxstsqlvl == 0); - - //fprintf(stderr, "Got RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); - - /* Read RXSTS */ - grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); - //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); - - /* Clear RXSTSQLVL in GINTSTS */ - gintsts.d32 = 0; - gintsts.b.rxstsqlvl = 1; - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - switch (grxsts.b.pktsts) { - case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: - break; - - default: - //fprintf(stderr, "** Unexpected GRXSTS packet status 2 **\n"); - break; - } - - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "Waiting for HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); - - /* Wait for host channel interrupt */ - do { - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - } while (gintsts.b.hcintr == 0); - - //fprintf(stderr, "Got HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - // usleep(100000); - // mdelay(100); - MDELAY(1); - - /* - * Send handshake packet - */ - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Make sure channel is disabled */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - if (hcchar.b.chen) { - //fprintf(stderr, "Channel already enabled 3, HCCHAR = %08x\n", hcchar.d32); - hcchar.b.chdis = 1; - hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - //sleep(1); - MDELAY(1000); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //if (hcchar.b.chen) { - // fprintf(stderr, "** Channel _still_ enabled 3, HCCHAR = %08x **\n", hcchar.d32); - //} - } - - /* Set HCTSIZ */ - hctsiz.d32 = 0; - hctsiz.b.xfersize = 0; - hctsiz.b.pktcnt = 1; - hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; - dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); - - /* Set HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; - hcchar.b.epdir = 0; - hcchar.b.epnum = 0; - hcchar.b.mps = 8; - hcchar.b.chen = 1; - dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); - - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "Waiting for HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); - - /* Wait for host channel interrupt */ - do { - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - } while (gintsts.b.hcintr == 0); - - //fprintf(stderr, "Got HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); - - /* Disable HCINTs */ - dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); - - /* Disable HAINTs */ - dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); - - /* Read HAINT */ - haint.d32 = dwc_read_reg32(&hc_global_regs->haint); - //fprintf(stderr, "HAINT: %08x\n", haint.d32); - - /* Read HCINT */ - hcint.d32 = dwc_read_reg32(&hc_regs->hcint); - //fprintf(stderr, "HCINT: %08x\n", hcint.d32); - - /* Read HCCHAR */ - hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); - //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); - - /* Clear HCINT */ - dwc_write_reg32(&hc_regs->hcint, hcint.d32); - - /* Clear HAINT */ - dwc_write_reg32(&hc_global_regs->haint, haint.d32); - - /* Clear GINTSTS */ - dwc_write_reg32(&global_regs->gintsts, gintsts.d32); - - /* Read GINTSTS */ - gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); - //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); -} -#endif /* DWC_HS_ELECT_TST */ - -/** Handles hub class-specific requests.*/ -int dwc_otg_hcd_hub_control(struct usb_hcd *_hcd, - u16 _typeReq, - u16 _wValue, - u16 _wIndex, - char *_buf, - u16 _wLength) -{ - int retval = 0; - - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); - dwc_otg_core_if_t *core_if = hcd_to_dwc_otg_hcd (_hcd)->core_if; - struct usb_hub_descriptor *desc; - hprt0_data_t hprt0 = {.d32 = 0}; - - uint32_t port_status; - - switch (_typeReq) { - case ClearHubFeature: - DWC_DEBUGPL (DBG_HCD, "DWC OTG 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; - DWC_ERROR ("DWC OTG HCD - " - "ClearHubFeature request %xh unknown\n", _wValue); - } - break; - case ClearPortFeature: - if (!_wIndex || _wIndex > 1) - goto error; - - switch (_wValue) { - case USB_PORT_FEAT_ENABLE: - DWC_DEBUGPL (DBG_ANY, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtena = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - break; - case USB_PORT_FEAT_SUSPEND: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtres = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - /* Clear Resume bit */ - mdelay (100); - hprt0.b.prtres = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - break; - case USB_PORT_FEAT_POWER: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_POWER\n"); - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtpwr = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - break; - case USB_PORT_FEAT_INDICATOR: - DWC_DEBUGPL (DBG_HCD, "DWC OTG 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 */ - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); - dwc_otg_hcd->flags.b.port_connect_status_change = 0; - break; - case USB_PORT_FEAT_C_RESET: - /* Clears the driver's internal Port Reset Change - * flag */ - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); - dwc_otg_hcd->flags.b.port_reset_change = 0; - break; - case USB_PORT_FEAT_C_ENABLE: - /* Clears the driver's internal Port - * Enable/Disable Change flag */ - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); - dwc_otg_hcd->flags.b.port_enable_change = 0; - break; - case USB_PORT_FEAT_C_SUSPEND: - /* Clears the driver's internal Port Suspend - * Change flag, which is set when resume signaling on - * the host port is complete */ - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); - dwc_otg_hcd->flags.b.port_suspend_change = 0; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); - dwc_otg_hcd->flags.b.port_over_current_change = 0; - break; - default: - retval = -EINVAL; - DWC_ERROR ("DWC OTG HCD - " - "ClearPortFeature request %xh " - "unknown or unsupported\n", _wValue); - } - break; - case GetHubDescriptor: - DWC_DEBUGPL (DBG_HCD, "DWC OTG 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] = 0xff; - break; - case GetHubStatus: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "GetHubStatus\n"); - memset (_buf, 0, 4); - break; - case GetPortStatus: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "GetPortStatus\n"); - - if (!_wIndex || _wIndex > 1) - goto error; - - port_status = 0; - - if (dwc_otg_hcd->flags.b.port_connect_status_change) - port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); - - if (dwc_otg_hcd->flags.b.port_enable_change) - port_status |= (1 << USB_PORT_FEAT_C_ENABLE); - - if (dwc_otg_hcd->flags.b.port_suspend_change) - port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); - - if (dwc_otg_hcd->flags.b.port_reset_change) - port_status |= (1 << USB_PORT_FEAT_C_RESET); - - if (dwc_otg_hcd->flags.b.port_over_current_change) { - DWC_ERROR("Device Not Supported\n"); - port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT); - } - - if (!dwc_otg_hcd->flags.b.port_connect_status) { - printk("DISCONNECTED PORT\n"); - /* - * 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. - */ -#if 1 // winder. - *((u32 *) _buf) = cpu_to_le32(port_status); -#else - *((__le32 *) _buf) = cpu_to_le32(port_status); -#endif - break; - } - - hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); - DWC_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 == DWC_HPRT0_PRTSPD_HIGH_SPEED) - port_status |= USB_PORT_STAT_HIGH_SPEED; - - else if (hprt0.b.prtspd == DWC_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 */ -#if 1 // winder. - *((u32 *) _buf) = cpu_to_le32(port_status); -#else - *((__le32 *) _buf) = cpu_to_le32(port_status); -#endif - - break; - case SetHubFeature: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetHubFeature\n"); - /* No HUB features supported */ - break; - case SetPortFeature: - if (_wValue != USB_PORT_FEAT_TEST && (!_wIndex || _wIndex > 1)) - goto error; - - if (!dwc_otg_hcd->flags.b.port_connect_status) { - /* - * The port is disconnected, which means the core is - * either in device mode or it soon will be. Just - * return without doing anything since the port - * register can't be written if the core is in device - * mode. - */ - break; - } - - switch (_wValue) { - case USB_PORT_FEAT_SUSPEND: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); - if (_hcd->self.otg_port == _wIndex - && _hcd->self.b_hnp_enable) { - gotgctl_data_t gotgctl = {.d32=0}; - gotgctl.b.hstsethnpen = 1; - dwc_modify_reg32(&core_if->core_global_regs-> - gotgctl, 0, gotgctl.d32); - core_if->op_state = A_SUSPEND; - } - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtsusp = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - //DWC_PRINT( "SUSPEND: HPRT0=%0x\n", hprt0.d32); - /* Suspend the Phy Clock */ - { - pcgcctl_data_t pcgcctl = {.d32=0}; - pcgcctl.b.stoppclk = 1; - dwc_write_reg32(core_if->pcgcctl, pcgcctl.d32); - } - - /* For HNP the bus must be suspended for at least 200ms.*/ - if (_hcd->self.b_hnp_enable) { - mdelay(200); - //DWC_PRINT( "SUSPEND: wait complete! (%d)\n", _hcd->state); - } - break; - case USB_PORT_FEAT_POWER: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetPortFeature - USB_PORT_FEAT_POWER\n"); - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtpwr = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - break; - case USB_PORT_FEAT_RESET: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetPortFeature - USB_PORT_FEAT_RESET\n"); - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - /* TODO: Is this for OTG protocol?? - * We shoudl remove OTG totally for Danube system. - * But, in the future, maybe we need this. - */ -#if 1 // winder - hprt0.b.prtrst = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); -#else - /* When B-Host the Port reset bit is set in - * the Start HCD Callback function, so that - * the reset is started within 1ms of the HNP - * success interrupt. */ - if (!_hcd->self.is_b_host) { - hprt0.b.prtrst = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - } -#endif - /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ - MDELAY (60); - hprt0.b.prtrst = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - break; - -#ifdef DWC_HS_ELECT_TST - case USB_PORT_FEAT_TEST: - { - uint32_t t; - gintmsk_data_t gintmsk; - - t = (_wIndex >> 8); /* MSB wIndex USB */ - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetPortFeature - USB_PORT_FEAT_TEST %d\n", t); - printk("USB_PORT_FEAT_TEST %d\n", t); - if (t < 6) { - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prttstctl = t; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - } else { - /* Setup global vars with reg addresses (quick and - * dirty hack, should be cleaned up) - */ - global_regs = core_if->core_global_regs; - hc_global_regs = core_if->host_if->host_global_regs; - hc_regs = (dwc_otg_hc_regs_t *)((char *)global_regs + 0x500); - data_fifo = (uint32_t *)((char *)global_regs + 0x1000); - - if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ - /* Save current interrupt mask */ - gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); - - /* Disable all interrupts while we muck with - * the hardware directly - */ - dwc_write_reg32(&global_regs->gintmsk, 0); - - /* 15 second delay per the test spec */ - mdelay(15000); - - /* Drive suspend on the root port */ - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtsusp = 1; - hprt0.b.prtres = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - - /* 15 second delay per the test spec */ - mdelay(15000); - - /* Drive resume on the root port */ - hprt0.d32 = dwc_otg_read_hprt0 (core_if); - hprt0.b.prtsusp = 0; - hprt0.b.prtres = 1; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - mdelay(100); - - /* Clear the resume bit */ - hprt0.b.prtres = 0; - dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); - - /* Restore interrupts */ - dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); - } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ - /* Save current interrupt mask */ - gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); - - /* Disable all interrupts while we muck with - * the hardware directly - */ - dwc_write_reg32(&global_regs->gintmsk, 0); - - /* 15 second delay per the test spec */ - mdelay(15000); - - /* Send the Setup packet */ - do_setup(); - - /* 15 second delay so nothing else happens for awhile */ - mdelay(15000); - - /* Restore interrupts */ - dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); - } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ - /* Save current interrupt mask */ - gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); - - /* Disable all interrupts while we muck with - * the hardware directly - */ - dwc_write_reg32(&global_regs->gintmsk, 0); - - /* Send the Setup packet */ - do_setup(); - - /* 15 second delay so nothing else happens for awhile */ - mdelay(15000); - - /* Send the In and Ack packets */ - do_in_ack(); - - /* 15 second delay so nothing else happens for awhile */ - mdelay(15000); - - /* Restore interrupts */ - dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); - } - } - break; - } -#endif /* DWC_HS_ELECT_TST */ - - case USB_PORT_FEAT_INDICATOR: - DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " - "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); - /* Not supported */ - break; - default: - retval = -EINVAL; - DWC_ERROR ("DWC OTG HCD - " - "SetPortFeature request %xh " - "unknown or unsupported\n", _wValue); - break; - } - break; - default: -error: - retval = -EINVAL; - DWC_WARN ("DWC OTG HCD - " - "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", - _typeReq, _wIndex, _wValue); - break; - } - - return retval; -} - - -/** - * Assigns transactions from a QTD to a free host channel and initializes the - * host channel to perform the transactions. The host channel is removed from - * the free list. - * - * @param _hcd The HCD state structure. - * @param _qh Transactions from the first QTD for this QH are selected and - * assigned to a free host channel. - */ -static void assign_and_init_hc(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) -{ - dwc_hc_t *hc; - dwc_otg_qtd_t *qtd; - struct urb *urb; - - DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, _hcd, _qh); - - hc = list_entry(_hcd->free_hc_list.next, dwc_hc_t, hc_list_entry); - - /* Remove the host channel from the free list. */ - list_del_init(&hc->hc_list_entry); - - qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); - urb = qtd->urb; - _qh->channel = hc; - _qh->qtd_in_process = qtd; - - /* - * Use usb_pipedevice to determine device address. This address is - * 0 before the SET_ADDRESS command and the correct address afterward. - */ - hc->dev_addr = usb_pipedevice(urb->pipe); - hc->ep_num = usb_pipeendpoint(urb->pipe); - - if (urb->dev->speed == USB_SPEED_LOW) { - hc->speed = DWC_OTG_EP_SPEED_LOW; - } else if (urb->dev->speed == USB_SPEED_FULL) { - hc->speed = DWC_OTG_EP_SPEED_FULL; - } else { - hc->speed = DWC_OTG_EP_SPEED_HIGH; - } - hc->max_packet = dwc_max_packet(_qh->maxp); - - hc->xfer_started = 0; - hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; - hc->error_state = (qtd->error_count > 0); - hc->halt_on_queue = 0; - hc->halt_pending = 0; - hc->requests = 0; - - /* - * The following values may be modified in the transfer type section - * below. The xfer_len value may be reduced when the transfer is - * started to accommodate the max widths of the XferSize and PktCnt - * fields in the HCTSIZn register. - */ - hc->do_ping = _qh->ping_state; - hc->ep_is_in = (usb_pipein(urb->pipe) != 0); - hc->data_pid_start = _qh->data_toggle; - hc->multi_count = 1; - - if (_hcd->core_if->dma_enable) { - hc->xfer_buff = (uint8_t *)(u32)urb->transfer_dma + urb->actual_length; - } else { - hc->xfer_buff = (uint8_t *)urb->transfer_buffer + urb->actual_length; - } - hc->xfer_len = urb->transfer_buffer_length - urb->actual_length; - hc->xfer_count = 0; - - /* - * Set the split attributes - */ - hc->do_split = 0; - if (_qh->do_split) { - hc->do_split = 1; - hc->xact_pos = qtd->isoc_split_pos; - hc->complete_split = qtd->complete_split; - hc->hub_addr = urb->dev->tt->hub->devnum; - hc->port_addr = urb->dev->ttport; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; - switch (qtd->control_phase) { - case DWC_OTG_CONTROL_SETUP: - DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); - hc->do_ping = 0; - hc->ep_is_in = 0; - hc->data_pid_start = DWC_OTG_HC_PID_SETUP; - if (_hcd->core_if->dma_enable) { - hc->xfer_buff = (uint8_t *)(u32)urb->setup_dma; - } else { - hc->xfer_buff = (uint8_t *)urb->setup_packet; - } - hc->xfer_len = 8; - break; - case DWC_OTG_CONTROL_DATA: - DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); - hc->data_pid_start = qtd->data_toggle; - break; - case DWC_OTG_CONTROL_STATUS: - /* - * Direction is opposite of data direction or IN if no - * data. - */ - DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); - if (urb->transfer_buffer_length == 0) { - hc->ep_is_in = 1; - } else { - hc->ep_is_in = (usb_pipein(urb->pipe) != USB_DIR_IN); - } - if (hc->ep_is_in) { - hc->do_ping = 0; - } - hc->data_pid_start = DWC_OTG_HC_PID_DATA1; - hc->xfer_len = 0; - if (_hcd->core_if->dma_enable) { - hc->xfer_buff = (uint8_t *)_hcd->status_buf_dma; - } else { - hc->xfer_buff = (uint8_t *)_hcd->status_buf; - } - break; - } - break; - case PIPE_BULK: - hc->ep_type = DWC_OTG_EP_TYPE_BULK; - break; - case PIPE_INTERRUPT: - hc->ep_type = DWC_OTG_EP_TYPE_INTR; - break; - case PIPE_ISOCHRONOUS: - { - struct usb_iso_packet_descriptor *frame_desc; - frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index]; - hc->ep_type = DWC_OTG_EP_TYPE_ISOC; - if (_hcd->core_if->dma_enable) { - hc->xfer_buff = (uint8_t *)(u32)urb->transfer_dma; - } else { - hc->xfer_buff = (uint8_t *)urb->transfer_buffer; - } - hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset; - hc->xfer_len = frame_desc->length - qtd->isoc_split_offset; - - if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { - if (hc->xfer_len <= 188) { - hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; - } - else { - hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN; - } - } - } - break; - } - - if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || - hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { - /* - * This value may be modified when the transfer is started to - * reflect the actual transfer length. - */ - hc->multi_count = dwc_hb_mult(_qh->maxp); - } - - dwc_otg_hc_init(_hcd->core_if, hc); - hc->qh = _qh; -} -#define DEBUG_HOST_CHANNELS -#ifdef DEBUG_HOST_CHANNELS -static int last_sel_trans_num_per_scheduled = 0; -module_param(last_sel_trans_num_per_scheduled, int, 0444); - -static int last_sel_trans_num_nonper_scheduled = 0; -module_param(last_sel_trans_num_nonper_scheduled, int, 0444); - -static int last_sel_trans_num_avail_hc_at_start = 0; -module_param(last_sel_trans_num_avail_hc_at_start, int, 0444); - -static int last_sel_trans_num_avail_hc_at_end = 0; -module_param(last_sel_trans_num_avail_hc_at_end, int, 0444); -#endif /* DEBUG_HOST_CHANNELS */ - -/** - * This function selects transactions from the HCD transfer schedule and - * assigns them to available host channels. It is called from HCD interrupt - * handler functions. - * - * @param _hcd The HCD state structure. - * - * @return The types of new transactions that were assigned to host channels. - */ -dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *_hcd) -{ - struct list_head *qh_ptr; - dwc_otg_qh_t *qh; - int num_channels; - unsigned long flags; - dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; - -#ifdef DEBUG_SOF - DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); -#endif /* */ - -#ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_per_scheduled = 0; - last_sel_trans_num_nonper_scheduled = 0; - last_sel_trans_num_avail_hc_at_start = _hcd->available_host_channels; -#endif /* DEBUG_HOST_CHANNELS */ - - /* Process entries in the periodic ready list. */ - num_channels = _hcd->core_if->core_params->host_channels; - qh_ptr = _hcd->periodic_sched_ready.next; - while (qh_ptr != &_hcd->periodic_sched_ready - && !list_empty(&_hcd->free_hc_list)) { - - // Make sure we leave one channel for non periodic transactions. - local_irq_save(flags); - if (_hcd->available_host_channels <= 1) { - local_irq_restore(flags); - break; - } - _hcd->available_host_channels--; - local_irq_restore(flags); -#ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_per_scheduled++; -#endif /* DEBUG_HOST_CHANNELS */ - - qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); - assign_and_init_hc(_hcd, qh); - - /* - * Move the QH from the periodic ready schedule to the - * periodic assigned schedule. - */ - qh_ptr = qh_ptr->next; - local_irq_save(flags); - list_move(&qh->qh_list_entry, &_hcd->periodic_sched_assigned); - local_irq_restore(flags); - ret_val = DWC_OTG_TRANSACTION_PERIODIC; - } - - /* - * Process entries in the deferred portion of the non-periodic list. - * A NAK put them here and, at the right time, they need to be - * placed on the sched_inactive list. - */ - qh_ptr = _hcd->non_periodic_sched_deferred.next; - while (qh_ptr != &_hcd->non_periodic_sched_deferred) { - uint16_t frame_number = - dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(_hcd)); - qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); - qh_ptr = qh_ptr->next; - - if (dwc_frame_num_le(qh->sched_frame, frame_number)) { - // NAK did this - /* - * Move the QH from the non periodic deferred schedule to - * the non periodic inactive schedule. - */ - local_irq_save(flags); - list_move(&qh->qh_list_entry, - &_hcd->non_periodic_sched_inactive); - local_irq_restore(flags); - } - } - - /* - * Process entries in the inactive portion of the non-periodic - * schedule. Some free host channels may not be used if they are - * reserved for periodic transfers. - */ - qh_ptr = _hcd->non_periodic_sched_inactive.next; - num_channels = _hcd->core_if->core_params->host_channels; - while (qh_ptr != &_hcd->non_periodic_sched_inactive - && !list_empty(&_hcd->free_hc_list)) { - - local_irq_save(flags); - if (_hcd->available_host_channels < 1) { - local_irq_restore(flags); - break; - } - _hcd->available_host_channels--; - local_irq_restore(flags); -#ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_nonper_scheduled++; -#endif /* DEBUG_HOST_CHANNELS */ - - qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); - assign_and_init_hc(_hcd, qh); - - /* - * Move the QH from the non-periodic inactive schedule to the - * non-periodic active schedule. - */ - qh_ptr = qh_ptr->next; - local_irq_save(flags); - list_move(&qh->qh_list_entry, &_hcd->non_periodic_sched_active); - local_irq_restore(flags); - - if (ret_val == DWC_OTG_TRANSACTION_NONE) { - ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; - } else { - ret_val = DWC_OTG_TRANSACTION_ALL; - } - - } -#ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_avail_hc_at_end = _hcd->available_host_channels; -#endif /* DEBUG_HOST_CHANNELS */ - - return ret_val; -} - -/** - * Attempts to queue a single transaction request for a host channel - * associated with either a periodic or non-periodic transfer. This function - * assumes that there is space available in the appropriate request queue. For - * an OUT transfer or SETUP transaction in Slave mode, it checks whether space - * is available in the appropriate Tx FIFO. - * - * @param _hcd The HCD state structure. - * @param _hc Host channel descriptor associated with either a periodic or - * non-periodic transfer. - * @param _fifo_dwords_avail Number of DWORDs available in the periodic Tx - * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic - * transfers. - * - * @return 1 if a request is queued and more requests may be needed to - * complete the transfer, 0 if no more requests are required for this - * transfer, -1 if there is insufficient space in the Tx FIFO. - */ -static int queue_transaction(dwc_otg_hcd_t *_hcd, - dwc_hc_t *_hc, - uint16_t _fifo_dwords_avail) -{ - int retval; - - if (_hcd->core_if->dma_enable) { - if (!_hc->xfer_started) { - dwc_otg_hc_start_transfer(_hcd->core_if, _hc); - _hc->qh->ping_state = 0; - } - retval = 0; - } else if (_hc->halt_pending) { - /* Don't queue a request if the channel has been halted. */ - retval = 0; - } else if (_hc->halt_on_queue) { - dwc_otg_hc_halt(_hcd->core_if, _hc, _hc->halt_status); - retval = 0; - } else if (_hc->do_ping) { - if (!_hc->xfer_started) { - dwc_otg_hc_start_transfer(_hcd->core_if, _hc); - } - retval = 0; - } else if (!_hc->ep_is_in || - _hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { - if ((_fifo_dwords_avail * 4) >= _hc->max_packet) { - if (!_hc->xfer_started) { - dwc_otg_hc_start_transfer(_hcd->core_if, _hc); - retval = 1; - } else { - retval = dwc_otg_hc_continue_transfer(_hcd->core_if, _hc); - } - } else { - retval = -1; - } - } else { - if (!_hc->xfer_started) { - dwc_otg_hc_start_transfer(_hcd->core_if, _hc); - retval = 1; - } else { - retval = dwc_otg_hc_continue_transfer(_hcd->core_if, _hc); - } - } - - return retval; -} - -/** - * Processes active non-periodic channels and queues transactions for these - * channels to the DWC_otg controller. After queueing transactions, the NP Tx - * FIFO Empty interrupt is enabled if there are more transactions to queue as - * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx - * FIFO Empty interrupt is disabled. - */ -static void process_non_periodic_channels(dwc_otg_hcd_t *_hcd) -{ - gnptxsts_data_t tx_status; - struct list_head *orig_qh_ptr; - dwc_otg_qh_t *qh; - int status; - int no_queue_space = 0; - int no_fifo_space = 0; - int more_to_do = 0; - - dwc_otg_core_global_regs_t *global_regs = _hcd->core_if->core_global_regs; - - DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n"); -#ifdef DEBUG - tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); - DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (before queue): %d\n", - tx_status.b.nptxqspcavail); - DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n", - tx_status.b.nptxfspcavail); -#endif - /* - * Keep track of the starting point. Skip over the start-of-list - * entry. - */ - if (_hcd->non_periodic_qh_ptr == &_hcd->non_periodic_sched_active) { - _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; - } - orig_qh_ptr = _hcd->non_periodic_qh_ptr; - - /* - * Process once through the active list or until no more space is - * available in the request queue or the Tx FIFO. - */ - do { - tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); - if (!_hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) { - no_queue_space = 1; - break; - } - - qh = list_entry(_hcd->non_periodic_qh_ptr, dwc_otg_qh_t, qh_list_entry); - status = queue_transaction(_hcd, qh->channel, tx_status.b.nptxfspcavail); - - if (status > 0) { - more_to_do = 1; - } else if (status < 0) { - no_fifo_space = 1; - break; - } - - /* Advance to next QH, skipping start-of-list entry. */ - _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; - if (_hcd->non_periodic_qh_ptr == &_hcd->non_periodic_sched_active) { - _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; - } - - } while (_hcd->non_periodic_qh_ptr != orig_qh_ptr); - - if (!_hcd->core_if->dma_enable) { - gintmsk_data_t intr_mask = {.d32 = 0}; - intr_mask.b.nptxfempty = 1; - -#ifdef DEBUG - tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); - DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (after queue): %d\n", - tx_status.b.nptxqspcavail); - DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (after queue): %d\n", - tx_status.b.nptxfspcavail); -#endif - if (more_to_do || no_queue_space || no_fifo_space) { - /* - * May need to queue more transactions as the request - * queue or Tx FIFO empties. Enable the non-periodic - * Tx FIFO empty interrupt. (Always use the half-empty - * level to ensure that new requests are loaded as - * soon as possible.) - */ - dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); - } else { - /* - * Disable the Tx FIFO empty interrupt since there are - * no more transactions that need to be queued right - * now. This function is called from interrupt - * handlers to queue more transactions as transfer - * states change. - */ - dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); - } - } -} - -/** - * Processes periodic channels for the next frame and queues transactions for - * these channels to the DWC_otg controller. After queueing transactions, the - * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions - * to queue as Periodic Tx FIFO or request queue space becomes available. - * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. - */ -static void process_periodic_channels(dwc_otg_hcd_t *_hcd) -{ - hptxsts_data_t tx_status; - struct list_head *qh_ptr; - dwc_otg_qh_t *qh; - int status; - int no_queue_space = 0; - int no_fifo_space = 0; - - dwc_otg_host_global_regs_t *host_regs; - host_regs = _hcd->core_if->host_if->host_global_regs; - - DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n"); -#ifdef DEBUG - tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); - DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (before queue): %d\n", - tx_status.b.ptxqspcavail); - DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n", - tx_status.b.ptxfspcavail); -#endif - - qh_ptr = _hcd->periodic_sched_assigned.next; - while (qh_ptr != &_hcd->periodic_sched_assigned) { - tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); - if (tx_status.b.ptxqspcavail == 0) { - no_queue_space = 1; - break; - } - - qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); - - /* - * Set a flag if we're queuing high-bandwidth in slave mode. - * The flag prevents any halts to get into the request queue in - * the middle of multiple high-bandwidth packets getting queued. - */ - if ((!_hcd->core_if->dma_enable) && - (qh->channel->multi_count > 1)) - { - _hcd->core_if->queuing_high_bandwidth = 1; - } - - status = queue_transaction(_hcd, qh->channel, tx_status.b.ptxfspcavail); - if (status < 0) { - no_fifo_space = 1; - break; - } - - /* - * In Slave mode, stay on the current transfer until there is - * nothing more to do or the high-bandwidth request count is - * reached. In DMA mode, only need to queue one request. The - * controller automatically handles multiple packets for - * high-bandwidth transfers. - */ - if (_hcd->core_if->dma_enable || - (status == 0 || - qh->channel->requests == qh->channel->multi_count)) { - qh_ptr = qh_ptr->next; - /* - * Move the QH from the periodic assigned schedule to - * the periodic queued schedule. - */ - list_move(&qh->qh_list_entry, &_hcd->periodic_sched_queued); - - /* done queuing high bandwidth */ - _hcd->core_if->queuing_high_bandwidth = 0; - } - } - - if (!_hcd->core_if->dma_enable) { - dwc_otg_core_global_regs_t *global_regs; - gintmsk_data_t intr_mask = {.d32 = 0}; - - global_regs = _hcd->core_if->core_global_regs; - intr_mask.b.ptxfempty = 1; -#ifdef DEBUG - tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); - DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (after queue): %d\n", - tx_status.b.ptxqspcavail); - DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (after queue): %d\n", - tx_status.b.ptxfspcavail); -#endif - if (!(list_empty(&_hcd->periodic_sched_assigned)) || - no_queue_space || no_fifo_space) { - /* - * May need to queue more transactions as the request - * queue or Tx FIFO empties. Enable the periodic Tx - * FIFO empty interrupt. (Always use the half-empty - * level to ensure that new requests are loaded as - * soon as possible.) - */ - dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); - } else { - /* - * Disable the Tx FIFO empty interrupt since there are - * no more transactions that need to be queued right - * now. This function is called from interrupt - * handlers to queue more transactions as transfer - * states change. - */ - dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); - } - } -} - -/** - * This function processes the currently active host channels and queues - * transactions for these channels to the DWC_otg controller. It is called - * from HCD interrupt handler functions. - * - * @param _hcd The HCD state structure. - * @param _tr_type The type(s) of transactions to queue (non-periodic, - * periodic, or both). - */ -void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *_hcd, - dwc_otg_transaction_type_e _tr_type) -{ -#ifdef DEBUG_SOF - DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n"); -#endif - /* Process host channels associated with periodic transfers. */ - if ((_tr_type == DWC_OTG_TRANSACTION_PERIODIC || - _tr_type == DWC_OTG_TRANSACTION_ALL) && - !list_empty(&_hcd->periodic_sched_assigned)) { - - process_periodic_channels(_hcd); - } - - /* Process host channels associated with non-periodic transfers. */ - if ((_tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC || - _tr_type == DWC_OTG_TRANSACTION_ALL)) { - if (!list_empty(&_hcd->non_periodic_sched_active)) { - process_non_periodic_channels(_hcd); - } else { - /* - * Ensure NP Tx FIFO empty interrupt is disabled when - * there are no non-periodic transfers to process. - */ - gintmsk_data_t gintmsk = {.d32 = 0}; - gintmsk.b.nptxfempty = 1; - dwc_modify_reg32(&_hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); - } - } -} - -/** - * Sets the final status of an URB and returns it to the device driver. Any - * required cleanup of the URB is performed. - */ -void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t * _hcd, struct urb *_urb, - int _status) - __releases(_hcd->lock) -__acquires(_hcd->lock) -{ -#ifdef DEBUG - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - DWC_PRINT("%s: urb %p, device %d, ep %d %s, status=%d\n", - __func__, _urb, usb_pipedevice(_urb->pipe), - usb_pipeendpoint(_urb->pipe), - usb_pipein(_urb->pipe) ? "IN" : "OUT", _status); - if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) { - int i; - for (i = 0; i < _urb->number_of_packets; i++) { - DWC_PRINT(" ISO Desc %d status: %d\n", - i, _urb->iso_frame_desc[i].status); - } - } - } -#endif - - _urb->status = _status; - _urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(_hcd), _urb); - spin_unlock(&_hcd->lock); - usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(_hcd), _urb, _status); - spin_lock(&_hcd->lock); -} - -/* - * Returns the Queue Head for an URB. - */ -dwc_otg_qh_t *dwc_urb_to_qh(struct urb *_urb) -{ - struct usb_host_endpoint *ep = dwc_urb_to_endpoint(_urb); - return (dwc_otg_qh_t *)ep->hcpriv; -} - -#ifdef DEBUG -void dwc_print_setup_data (uint8_t *setup) -{ - int i; - if (CHK_DEBUG_LEVEL(DBG_HCD)){ - DWC_PRINT("Setup Data = MSB "); - for (i=7; i>=0; i--) DWC_PRINT ("%02x ", setup[i]); - DWC_PRINT("\n"); - DWC_PRINT(" bmRequestType Tranfer = %s\n", (setup[0]&0x80) ? "Device-to-Host" : "Host-to-Device"); - DWC_PRINT(" bmRequestType Type = "); - switch ((setup[0]&0x60) >> 5) { - case 0: DWC_PRINT("Standard\n"); break; - case 1: DWC_PRINT("Class\n"); break; - case 2: DWC_PRINT("Vendor\n"); break; - case 3: DWC_PRINT("Reserved\n"); break; - } - DWC_PRINT(" bmRequestType Recipient = "); - switch (setup[0]&0x1f) { - case 0: DWC_PRINT("Device\n"); break; - case 1: DWC_PRINT("Interface\n"); break; - case 2: DWC_PRINT("Endpoint\n"); break; - case 3: DWC_PRINT("Other\n"); break; - default: DWC_PRINT("Reserved\n"); break; - } - DWC_PRINT(" bRequest = 0x%0x\n", setup[1]); - DWC_PRINT(" wValue = 0x%0x\n", *((uint16_t *)&setup[2])); - DWC_PRINT(" wIndex = 0x%0x\n", *((uint16_t *)&setup[4])); - DWC_PRINT(" wLength = 0x%0x\n\n", *((uint16_t *)&setup[6])); - } -} -#endif - -void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *_hcd) { -#ifdef DEBUG -#if 0 - DWC_PRINT("Frame remaining at SOF:\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->frrem_samples, _hcd->frrem_accum, - (_hcd->frrem_samples > 0) ? - _hcd->frrem_accum/_hcd->frrem_samples : 0); - - DWC_PRINT("\n"); - DWC_PRINT("Frame remaining at start_transfer (uframe 7):\n"); - DWC_PRINT(" samples %u, accum %u, avg %u\n", - _hcd->core_if->hfnum_7_samples, _hcd->core_if->hfnum_7_frrem_accum, - (_hcd->core_if->hfnum_7_samples > 0) ? - _hcd->core_if->hfnum_7_frrem_accum/_hcd->core_if->hfnum_7_samples : 0); - DWC_PRINT("Frame remaining at start_transfer (uframe 0):\n"); - DWC_PRINT(" samples %u, accum %u, avg %u\n", - _hcd->core_if->hfnum_0_samples, _hcd->core_if->hfnum_0_frrem_accum, - (_hcd->core_if->hfnum_0_samples > 0) ? - _hcd->core_if->hfnum_0_frrem_accum/_hcd->core_if->hfnum_0_samples : 0); - DWC_PRINT("Frame remaining at start_transfer (uframe 1-6):\n"); - DWC_PRINT(" samples %u, accum %u, avg %u\n", - _hcd->core_if->hfnum_other_samples, _hcd->core_if->hfnum_other_frrem_accum, - (_hcd->core_if->hfnum_other_samples > 0) ? - _hcd->core_if->hfnum_other_frrem_accum/_hcd->core_if->hfnum_other_samples : 0); - - DWC_PRINT("\n"); - DWC_PRINT("Frame remaining at sample point A (uframe 7):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_7_samples_a, _hcd->hfnum_7_frrem_accum_a, - (_hcd->hfnum_7_samples_a > 0) ? - _hcd->hfnum_7_frrem_accum_a/_hcd->hfnum_7_samples_a : 0); - DWC_PRINT("Frame remaining at sample point A (uframe 0):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_0_samples_a, _hcd->hfnum_0_frrem_accum_a, - (_hcd->hfnum_0_samples_a > 0) ? - _hcd->hfnum_0_frrem_accum_a/_hcd->hfnum_0_samples_a : 0); - DWC_PRINT("Frame remaining at sample point A (uframe 1-6):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_other_samples_a, _hcd->hfnum_other_frrem_accum_a, - (_hcd->hfnum_other_samples_a > 0) ? - _hcd->hfnum_other_frrem_accum_a/_hcd->hfnum_other_samples_a : 0); - - DWC_PRINT("\n"); - DWC_PRINT("Frame remaining at sample point B (uframe 7):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_7_samples_b, _hcd->hfnum_7_frrem_accum_b, - (_hcd->hfnum_7_samples_b > 0) ? - _hcd->hfnum_7_frrem_accum_b/_hcd->hfnum_7_samples_b : 0); - DWC_PRINT("Frame remaining at sample point B (uframe 0):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_0_samples_b, _hcd->hfnum_0_frrem_accum_b, - (_hcd->hfnum_0_samples_b > 0) ? - _hcd->hfnum_0_frrem_accum_b/_hcd->hfnum_0_samples_b : 0); - DWC_PRINT("Frame remaining at sample point B (uframe 1-6):\n"); - DWC_PRINT(" samples %u, accum %llu, avg %llu\n", - _hcd->hfnum_other_samples_b, _hcd->hfnum_other_frrem_accum_b, - (_hcd->hfnum_other_samples_b > 0) ? - _hcd->hfnum_other_frrem_accum_b/_hcd->hfnum_other_samples_b : 0); -#endif -#endif -} - -void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *_hcd) -{ -#ifdef DEBUG - int num_channels; - int i; - gnptxsts_data_t np_tx_status; - hptxsts_data_t p_tx_status; - - num_channels = _hcd->core_if->core_params->host_channels; - DWC_PRINT("\n"); - DWC_PRINT("************************************************************\n"); - DWC_PRINT("HCD State:\n"); - DWC_PRINT(" Num channels: %d\n", num_channels); - for (i = 0; i < num_channels; i++) { - dwc_hc_t *hc = _hcd->hc_ptr_array[i]; - DWC_PRINT(" Channel %d:\n", i); - DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", - hc->dev_addr, hc->ep_num, hc->ep_is_in); - DWC_PRINT(" speed: %d\n", hc->speed); - DWC_PRINT(" ep_type: %d\n", hc->ep_type); - DWC_PRINT(" max_packet: %d\n", hc->max_packet); - DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); - DWC_PRINT(" multi_count: %d\n", hc->multi_count); - DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); - DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); - DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); - DWC_PRINT(" xfer_count: %d\n", hc->xfer_count); - DWC_PRINT(" halt_on_queue: %d\n", hc->halt_on_queue); - DWC_PRINT(" halt_pending: %d\n", hc->halt_pending); - DWC_PRINT(" halt_status: %d\n", hc->halt_status); - DWC_PRINT(" do_split: %d\n", hc->do_split); - DWC_PRINT(" complete_split: %d\n", hc->complete_split); - DWC_PRINT(" hub_addr: %d\n", hc->hub_addr); - DWC_PRINT(" port_addr: %d\n", hc->port_addr); - DWC_PRINT(" xact_pos: %d\n", hc->xact_pos); - DWC_PRINT(" requests: %d\n", hc->requests); - DWC_PRINT(" qh: %p\n", hc->qh); - if (hc->xfer_started) { - hfnum_data_t hfnum; - hcchar_data_t hcchar; - hctsiz_data_t hctsiz; - hcint_data_t hcint; - hcintmsk_data_t hcintmsk; - hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); - hcchar.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcchar); - hctsiz.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hctsiz); - hcint.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcint); - hcintmsk.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcintmsk); - DWC_PRINT(" hfnum: 0x%08x\n", hfnum.d32); - DWC_PRINT(" hcchar: 0x%08x\n", hcchar.d32); - DWC_PRINT(" hctsiz: 0x%08x\n", hctsiz.d32); - DWC_PRINT(" hcint: 0x%08x\n", hcint.d32); - DWC_PRINT(" hcintmsk: 0x%08x\n", hcintmsk.d32); - } - if (hc->xfer_started && (hc->qh != NULL) && (hc->qh->qtd_in_process != NULL)) { - dwc_otg_qtd_t *qtd; - struct urb *urb; - qtd = hc->qh->qtd_in_process; - urb = qtd->urb; - DWC_PRINT(" URB Info:\n"); - DWC_PRINT(" qtd: %p, urb: %p\n", qtd, urb); - if (urb != NULL) { - DWC_PRINT(" Dev: %d, EP: %d %s\n", - usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "IN" : "OUT"); - DWC_PRINT(" Max packet size: %d\n", - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); - DWC_PRINT(" transfer_buffer: %p\n", urb->transfer_buffer); - DWC_PRINT(" transfer_dma: %p\n", (void *)urb->transfer_dma); - DWC_PRINT(" transfer_buffer_length: %d\n", urb->transfer_buffer_length); - DWC_PRINT(" actual_length: %d\n", urb->actual_length); - } - } - } - //DWC_PRINT(" non_periodic_channels: %d\n", _hcd->non_periodic_channels); - //DWC_PRINT(" periodic_channels: %d\n", _hcd->periodic_channels); - DWC_PRINT(" available_channels: %d\n", _hcd->available_host_channels); - DWC_PRINT(" periodic_usecs: %d\n", _hcd->periodic_usecs); - np_tx_status.d32 = dwc_read_reg32(&_hcd->core_if->core_global_regs->gnptxsts); - DWC_PRINT(" NP Tx Req Queue Space Avail: %d\n", np_tx_status.b.nptxqspcavail); - DWC_PRINT(" NP Tx FIFO Space Avail: %d\n", np_tx_status.b.nptxfspcavail); - p_tx_status.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hptxsts); - DWC_PRINT(" P Tx Req Queue Space Avail: %d\n", p_tx_status.b.ptxqspcavail); - DWC_PRINT(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail); - dwc_otg_hcd_dump_frrem(_hcd); - dwc_otg_dump_global_registers(_hcd->core_if); - dwc_otg_dump_host_registers(_hcd->core_if); - DWC_PRINT("************************************************************\n"); - DWC_PRINT("\n"); -#endif -} -#endif /* DWC_DEVICE_ONLY */ |