diff options
Diffstat (limited to 'drivers/usb/host/isp1763-hcd.c')
-rw-r--r-- | drivers/usb/host/isp1763-hcd.c | 6534 |
1 files changed, 6534 insertions, 0 deletions
diff --git a/drivers/usb/host/isp1763-hcd.c b/drivers/usb/host/isp1763-hcd.c new file mode 100644 index 00000000000..91fde67132a --- /dev/null +++ b/drivers/usb/host/isp1763-hcd.c @@ -0,0 +1,6534 @@ +/* +* Copyright (C) ST-Ericsson AP Pte Ltd 2010 +* +* ISP1763 Linux OTG Controller driver : host +* +* This program is free software; you can redistribute it and/or modify it under the terms of +* the GNU General Public License as published by the Free Software Foundation; version +* 2 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* Refer to the follwing files in ~/drivers/usb/host for copyright owners: +* ehci-dbg.c, ehci-hcd.c, ehci-hub.c, ehci-mem.c, ehci-q.c and ehic-sched.c (kernel version 2.6.9) +* Code is modified for ST-Ericsson product +* +* Author : wired support <wired.support@stericsson.com> +* +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +//#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/usb.h> +#include <linux/version.h> +#include <stdarg.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> +#include <linux/version.h> + +#include "isp1763.h" +#include "isp1763-hcd.h" +#include "isp1763-hal-interface.h" + +extern int No_Data_Phase; +extern int No_Status_Phase; +#define EHCI_TUNE_CERR 3 +#define URB_NO_INTERRUPT 0x0080 +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_RL_HS 0 +#define EHCI_TUNE_MULT_HS 1 + + +#define POWER_DOWN_CTRL_NORMAL_VALUE 0xffff1ba0 +#define POWER_DOWN_CTRL_SUSPEND_VALUE 0xffff08b0 + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) +//This macro is not supported in linux-2.6.35 +#define USB_PORT_FEAT_HIGHSPEED 10 +#endif + +#ifdef CONFIG_ISO_SUPPORT + +#define FALSE 0 +#define TRUE (!FALSE) +extern void *phcd_iso_sitd_to_ptd(phci_hcd * hcd, + struct ehci_sitd *sitd, + struct urb *urb, void *ptd); +extern void *phcd_iso_itd_to_ptd(phci_hcd * hcd, + struct ehci_itd *itd, + struct urb *urb, void *ptd); + +extern unsigned long phcd_submit_iso(phci_hcd * hcd, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + struct usb_host_endpoint *ep, +#else +#endif + struct urb *urb, unsigned long *status); +void pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *); +unsigned long lgFrameIndex = 0; +unsigned long lgScheduledPTDIndex = 0; +int igNumOfPkts = 0; +#endif /* CONFIG_ISO_SUPPORT */ + +struct isp1763_dev *isp1763_hcd; + +#ifdef HCD_PACKAGE +/*file operation*/ +struct fasync_struct *fasync_q; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static void +pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map, struct pt_regs *regs); +#else +static void +pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map); +#endif + +#include "isp1763-otg.c" /*OTG and HCD package needs it */ + + +int hcdpowerdown = 0; +int portchange=0; //for remotewakeup +EXPORT_SYMBOL(hcdpowerdown); +unsigned char otg_se0_enable; +EXPORT_SYMBOL(otg_se0_enable); + + +/*Enable all other interrupt.*/ + +#ifdef MSEC_INT_BASED +#ifdef THREAD_BASED//This is to test interrupt mapping problem +//#define INTR_ENABLE_MASK (HC_OPR_REG_INT|HC_CLK_RDY_INT ) +#define INTR_ENABLE_MASK (/*HC_MSEC_INT |*/ HC_INTL_INT | HC_ATL_INT| HC_ISO_INT /*| HC_EOT_INT | HC_ISO_INT*/) +#else +#define INTR_ENABLE_MASK (HC_MSEC_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT ) +#endif +#else +#define INTR_ENABLE_MASK ( HC_INTL_INT | HC_ATL_INT |HC_ISO_INT| HC_EOT_INT|HC_OPR_REG_INT|HC_CLK_RDY_INT) +#endif + + + +#ifdef THREAD_BASED + +#define NO_SOF_REQ_IN_TSK 0x1 +#define NO_SOF_REQ_IN_ISR 0x2 +#define NO_SOF_REQ_IN_REQ 0x3 +#define MSEC_INTERVAL_CHECKING 5 + +typedef struct _st_UsbIt_Msg_Struc { + struct usb_hcd *usb_hcd; + u8 uIntStatus; + struct list_head list; +} st_UsbIt_Msg_Struc, *pst_UsbIt_Msg_Struc ; + +typedef struct _st_UsbIt_Thread { + wait_queue_head_t ulThrdWaitQhead; + int lThrdWakeUpNeeded; + struct task_struct *phThreadTask; + spinlock_t lock; +} st_UsbIt_Thread, *pst_UsbIt_Thread; + +st_UsbIt_Thread g_stUsbItThreadHandler; + +st_UsbIt_Msg_Struc g_messList; +st_UsbIt_Msg_Struc g_enqueueMessList; +spinlock_t enqueue_lock; + +int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_); +int pehci_hcd_process_irq_in_thread(struct usb_hcd *usb_hcd_); + +#endif /*THREAD_BASED*/ + +#ifdef THREAD_BASED +phci_hcd *g_pehci_hcd; +#endif + + + +/*--------------------------------------------------- + * Globals for EHCI + -----------------------------------------------------*/ + +/* used when updating hcd data */ +//static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(hcd_data_lock); + +static const char hcd_name[] = "ST-Ericsson ISP1763"; +static td_ptd_map_buff_t td_ptd_map_buff[TD_PTD_TOTAL_BUFF_TYPES]; /* td-ptd map buffer for all 1362 buffers */ + +static u8 td_ptd_pipe_x_buff_type[TD_PTD_TOTAL_BUFF_TYPES] = { + TD_PTD_BUFF_TYPE_ATL, + TD_PTD_BUFF_TYPE_INTL, + TD_PTD_BUFF_TYPE_ISTL +}; + + +/*global memory blocks*/ +isp1763_mem_addr_t memalloc[BLK_TOTAL]; +#include "isp1763-mem.c" +#include "isp1763-qtdptd.c" + +#ifdef CONFIG_ISO_SUPPORT +#include "isp1763-itdptd.c" +#endif /* CONFIG_ISO_SUPPORT */ + +static int +pehci_rh_control(struct usb_hcd *usb_hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength); + +static int pehci_bus_suspend(struct usb_hcd *usb_hcd); +static int pehci_bus_resume(struct usb_hcd *usb_hcd); +/*----------------------------------------------------*/ +static void +pehci_complete_device_removal(phci_hcd * hcd, struct ehci_qh *qh) +{ + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *td_ptd_buff; + struct urb * urb; + urb_priv_t *urb_priv; + struct ehci_qtd *qtd = 0; +// struct usb_hcd *usb_hcd=&hcd->usb_hcd; + u16 skipmap=0; + + if (qh->type == TD_PTD_BUFF_TYPE_ISTL) { +#ifdef COMMON_MEMORY + phci_hcd_mem_free(&qh->memory_addr); +#endif + return; + } + + td_ptd_buff = &td_ptd_map_buff[qh->type]; + td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index]; + + /*this flag should only be set when device is going */ + td_ptd_map->state = TD_PTD_REMOVE; + /*if nothing there */ + if (list_empty(&qh->qtd_list)) { + if (td_ptd_map->state != TD_PTD_NEW) { + phci_hcd_release_td_ptd_index(qh); + } + qha_free(qha_cache, qh); + qh = 0; + return; + } else { + + if(!list_empty(&qh->qtd_list)){ + qtd=NULL; + qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list); + if(qtd){ + urb=qtd->urb; + urb_priv= urb->hcpriv; + + if(urb) + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + break; + case PIPE_INTERRUPT: + td_ptd_buff = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL]; + td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index]; + + /*urb is already been removed */ + // if (td_ptd_map->state == TD_PTD_NEW) { + // kfree(urb_priv); + // break; + // } + + /* These TDs are not pending anymore */ + td_ptd_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap; + + td_ptd_map->state = TD_PTD_REMOVE; + urb_priv->state |= DELETE_URB; + + /*read the skipmap, to see if this transfer has to be rescheduled */ + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, + skipmap | td_ptd_map->ptd_bitmap); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL); +#else + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map); +#endif + break; + } + + + }else{ + //break; + } + } + qha_free(qha_cache, qh); + qh = 0; + return; + } + /*MUST not come down below this */ + err("Never Error: Should not come to this portion of code\n"); + + return; +} + +/*functions looks for the values in register + specified in ptr, if register values masked + with the mask and result is equal to done, + operation is successful else fails with timeout*/ +static int +pehci_hcd_handshake(phci_hcd * hcd, u32 ptr, u32 mask, u32 done, int usec) +{ + u32 result = 0; + do { + result = isp1763_reg_read16(hcd->dev, ptr, result); + pehci_print(KERN_NOTICE "Registr %x val is %x\n", ptr, result); + if (result == ~(u32) 0) {/* card removed */ + return -ENODEV; + } + result &= mask; + if (result == done) { + return 0; + } + udelay(1); + usec--; + } while (usec > 0); + + return -ETIMEDOUT; +} + +#ifndef MSEC_INT_BASED +/*schedule atl and interrupt tds, + only when we are not running on sof interrupt + */ +static void +pehci_hcd_td_ptd_submit_urb(phci_hcd * hcd, struct ehci_qh *qh, u8 bufftype) +{ + unsigned long flags=0; + struct ehci_qtd *qtd = 0; + struct urb *urb = 0; + struct _isp1763_qha *qha = 0; + u16 location = 0; + u16 skipmap = 0; + u16 buffstatus = 0; + u16 ormask = 0; + u16 intormask = 0; + u32 length = 0; + struct list_head *head; + + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + struct isp1763_mem_addr *mem_addr = 0; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + pehci_print("Buuffer type %d\n", bufftype); + + spin_lock_irqsave(&hcd->lock, flags); + ptd_map_buff = &td_ptd_map_buff[bufftype]; + + qha = &hcd->qha; + + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + break; + case TD_PTD_BUFF_TYPE_INTL: + + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + + intormask = + isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or, + intormask); + break; + default: + + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, + skipmap); + break; + + } + + + buffstatus = + isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status, + buffstatus); + + /*header, qtd, and urb of current transfer */ + location = qh->qtd_ptd_index; + td_ptd_map = &ptd_map_buff->map_list[location]; + + if (!(qh->qh_state & QH_STATE_TAKE_NEXT)) { + pehci_check("qh will schdule from interrupt routine,map %x\n", + td_ptd_map->ptd_bitmap); + spin_unlock_irqrestore(&hcd->lock, flags); + return; + } + head = &qh->qtd_list; + qtd = list_entry(head->next, struct ehci_qtd, qtd_list); + + /*already scheduled, may be from interrupt */ + if (!(qtd->state & QTD_STATE_NEW)) { + pehci_check("qtd already in, state %x\n", qtd->state); + spin_unlock_irqrestore(&hcd->lock, flags); + return; + } + + qtd->state &= ~QTD_STATE_NEW; + qtd->state |= QTD_STATE_SCHEDULED; + + qh->qh_state &= ~QH_STATE_TAKE_NEXT; + /*take the first td */ + td_ptd_map->qtd = qtd; + /*take the urb */ + urb = qtd->urb; + ptd_map_buff->active_ptds++; + + /*trust the atl worker, at this location there wont be any td */ + /*if this td is the last one */ + if (qtd->state & QTD_STATE_LAST) { + qh->hw_current = cpu_to_le32(0); + /*else update the hw_next of qh to the next td */ + } else { + qh->hw_current = qtd->hw_next; + } + memset(qha, 0, sizeof(isp1763_qha)); + + pehci_check("td being scheduled : length: %d, device: %d, map: %x\n", + qtd->length, urb->dev->devnum, td_ptd_map->ptd_bitmap); + /*NEW, now need to get the memory for this transfer */ + length = qtd->length; + mem_addr = &qtd->mem_addr; + phci_hcd_mem_alloc(length, mem_addr, 0); + if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) { + err("Never Error: Can not allocate memory for the current td,length %d\n", length); + /*should not happen */ + /*can happen only when we exceed the limit of devices we support + MAX 4 mass storage at a time */ + } + phci_hcd_qha_from_qtd(hcd, qtd, qtd->urb, (void *) qha, + td_ptd_map->ptd_ram_data_addr, qh); + if (qh->type == TD_PTD_BUFF_TYPE_INTL) { + phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *) qha, + qtd->urb); + } + /*write qha into the header of the host controller */ + isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, 0); + + /*if this is SETUP/OUT token , then need to write into the buffer */ + /*length should be valid and supported by the ptd */ + if (qtd->length && (qtd->length <= HC_ATL_PL_SIZE)){ + switch (PTD_PID(qha->td_info2)) { + case OUT_PID: + case SETUP_PID: + + isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0, + (void *) qtd->hw_buf[0], length, 0); + + +#if 0 + int i=0; + int *data_addr= qtd->hw_buf[0]; + printk("\n"); + for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++); + printk("\n"); +#endif + + + + break; + } + } + + /*unskip the tds at this location */ + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + skipmap &= ~td_ptd_map->ptd_bitmap; + /*enable atl interrupts on donemap */ + ormask |= td_ptd_map->ptd_bitmap; + + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + break; + + case TD_PTD_BUFF_TYPE_INTL: + skipmap &= ~td_ptd_map->ptd_bitmap; + intormask |= td_ptd_map->ptd_bitmap; + + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, + intormask); + break; + + case TD_PTD_BUFF_TYPE_ISTL: + skipmap &= ~td_ptd_map->ptd_bitmap; + + isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap); + break; + } + + /*if any new schedule, enable the atl buffer */ + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | ATL_BUFFER); + + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap); + buffstatus |= ATL_BUFFER; + break; + case TD_PTD_BUFF_TYPE_INTL: + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | INT_BUFFER); + + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap); + break; + case TD_PTD_BUFF_TYPE_ISTL: + /*not supposed to be seen here */ + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | ISO_BUFFER); + break; + } + spin_unlock_irqrestore(&hcd->lock, flags); + pehci_entry("-- %s: Exit\n", __FUNCTION__); + return; + +} +#endif + + + +#ifdef MSEC_INT_BASED +/*schedule next (atl/int)tds and any pending tds*/ +static void +pehci_hcd_schedule_pending_ptds(phci_hcd * hcd, u16 donemap, u8 bufftype, + u16 only) +{ + struct ehci_qtd *qtd = 0; + struct ehci_qh *qh = 0; + struct list_head *qtd_list = 0; + struct _isp1763_qha allqha; + struct _isp1763_qha *qha = 0; + u16 mask = 0x1, index = 0; + u16 location = 0; + u16 skipmap = 0; + u32 newschedule = 0; + u16 buffstatus = 0; + u16 schedulemap = 0; +#ifndef CONFIG_ISO_SUPPORT + u16 lasttd = 1; +#endif + u16 lastmap = 0; + struct urb *urb = 0; + urb_priv_t *urbpriv = 0; + int length = 0; + u16 ormask = 0, andmask = 0; + u16 intormask = 0; + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + struct isp1763_mem_addr *mem_addr = 0; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + pehci_print("Buffer type %d\n", bufftype); + + /*need to hold this lock if another interrupt is comming + for previously scheduled transfer, while scheduling new tds + */ + spin_lock(&hcd_data_lock); + ptd_map_buff = &td_ptd_map_buff[bufftype]; + qha = &allqha; + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + rmb(); + + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + + andmask = + isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_and, + andmask); + break; + case TD_PTD_BUFF_TYPE_INTL: + + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + /*read the interrupt mask registers */ + + intormask = + isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or, + intormask); + break; + default: + err("Never Error: Bogus type of bufer\n"); + return; + } + + buffstatus = + isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status, + buffstatus); + /*td headers need attention */ + schedulemap = donemap; + while (schedulemap) { + index = schedulemap & mask; + schedulemap &= ~mask; + mask <<= 1; + + if (!index) { + location++; + continue; + } + + td_ptd_map = &ptd_map_buff->map_list[location]; + /* can happen if donemap comes after + removal of the urb and associated tds + */ + if ((td_ptd_map->state == TD_PTD_NEW) || + (td_ptd_map->state == TD_PTD_REMOVE)) { + qh = td_ptd_map->qh; + pehci_check + ("should not come here, map %x,pending map %x\n", + td_ptd_map->ptd_bitmap, + ptd_map_buff->pending_ptd_bitmap); + + pehci_check("buffer type %s\n", + (bufftype == 0) ? "ATL" : "INTL"); + donemap &= ~td_ptd_map->ptd_bitmap; + /*clear the pending map */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + /*no endpoint at this location */ + if (!(td_ptd_map->qh)) { + err("queue head can not be null here\n"); + /*move to the next location */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + /*current endpoint */ + qh = td_ptd_map->qh; + if (!(skipmap & td_ptd_map->ptd_bitmap)) { + /*should not happen, if happening, then */ + pehci_check("buffertype %d,td_ptd_map %x,skipnap %x\n", + bufftype, td_ptd_map->ptd_bitmap, skipmap); + lastmap = td_ptd_map->ptd_bitmap; + donemap &= ~td_ptd_map->ptd_bitmap; + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + /*if we processed all the tds in ths transfer */ + if (td_ptd_map->lasttd) { + err("should not show map %x,qtd %p\n", + td_ptd_map->ptd_bitmap, td_ptd_map->qtd); + /*this can happen in case the transfer is not being + * procesed by the host , tho the transfer is there + * */ + qh->hw_current = cpu_to_le32(td_ptd_map->qtd); + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + /*if we have ptd that is going for reload */ + if ((td_ptd_map->qtd) && (td_ptd_map->state & TD_PTD_RELOAD)) { + warn("%s: reload td\n", __FUNCTION__); + td_ptd_map->state &= ~TD_PTD_RELOAD; + qtd = td_ptd_map->qtd; + goto loadtd; + } + + /* qh is there but no qtd so it means fresh transfer */ + if ((td_ptd_map->qh) && !(td_ptd_map->qtd)) { + if (list_empty(&qh->qtd_list)) { + /*should not hapen again, as it comes here + when it has td in its map + */ + pehci_check + ("must not come here any more, td map %x\n", + td_ptd_map->ptd_bitmap); + /*this location is idle and can be free next time if + no new transfers are comming for this */ + donemap &= ~td_ptd_map->ptd_bitmap; + td_ptd_map->state |= TD_PTD_IDLE; + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + qtd_list = &qh->qtd_list; + qtd = td_ptd_map->qtd = + list_entry(qtd_list->next, struct ehci_qtd, + qtd_list); + /*got the td, now goto reload */ + goto loadtd; + } + + /*if there is already one qtd there in the transfer */ + if (td_ptd_map->qtd) { + /*new schedule */ + qtd = td_ptd_map->qtd; + } + loadtd: + /*should not happen */ + if (!qtd) { + err("this piece of code should not be executed\n"); + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + ptd_map_buff->active_ptds++; + /*clear the pending map here */ + ptd_map_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap; + + + + /*if this td is the last one */ + if (qtd->state & QTD_STATE_LAST) { + /*no qtd anymore */ + qh->hw_current = cpu_to_le32(0); + + /*else update the hw_next of qh to the next td */ + } else { + qh->hw_current = qtd->hw_next; + } + + if (location != qh->qtd_ptd_index) { + err("Never Error: Endpoint header location and scheduling information are not same\n"); + } + + /*next location */ + location++; + /*found new transfer */ + newschedule = 1; + /*take the urb */ + urb = qtd->urb; + /*sometimes we miss due to skipmap + so to make sure that we dont put again the + same stuff + */ + if (!(qtd->state & QTD_STATE_NEW)) { + err("Never Error: We should not put the same stuff\n"); + continue; + } + + urbpriv = (urb_priv_t *) urb->hcpriv; + urbpriv->timeout = 0; + + /*no more new */ + qtd->state &= ~QTD_STATE_NEW; + qtd->state |= QTD_STATE_SCHEDULED; + + + + /*NEW, now need to get the memory for this transfer */ + length = qtd->length; + mem_addr = &qtd->mem_addr; + phci_hcd_mem_alloc(length, mem_addr, 0); + if (length && ((mem_addr->phy_addr == 0) + || (mem_addr->virt_addr == 0))) { + + err("Never Error: Can not allocate memory for the current td,length %d\n", length); + location++; + continue; + } + + pehci_check("qtd being scheduled %p, device %d,map %x\n", qtd, + urb->dev->devnum, td_ptd_map->ptd_bitmap); + + + memset(qha, 0, sizeof(isp1763_qha)); + /*convert qtd to qha */ + phci_hcd_qha_from_qtd(hcd, qtd, qtd->urb, (void *) qha, + td_ptd_map->ptd_ram_data_addr, qh); + + if (qh->type == TD_PTD_BUFF_TYPE_INTL) { + phci_hcd_qhint_schedule(hcd, qh, qtd, + (isp1763_qhint *) qha, + qtd->urb); + + } + + + length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3); + if (length > HC_ATL_PL_SIZE) { + err("Never Error: Bogus length,length %d(max %d)\n", + qtd->length, HC_ATL_PL_SIZE); + } + + /*write qha into the header of the host controller */ + isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, 0); + +#ifdef PTD_DUMP_SCHEDULE + printk("SCHEDULE next (atl/int)tds PTD header\n"); + printk("DW0: 0x%08X\n", qha->td_info1); + printk("DW1: 0x%08X\n", qha->td_info2); + printk("DW2: 0x%08X\n", qha->td_info3); + printk("DW3: 0x%08X\n", qha->td_info4); +#endif + + /*if this is SETUP/OUT token , then need to write into the buffer */ + /*length should be valid */ + if (qtd->length && (length <= HC_ATL_PL_SIZE)){ + switch (PTD_PID(qha->td_info2)) { + case OUT_PID: + case SETUP_PID: + + isp1763_mem_write(hcd->dev, + (u32) mem_addr->phy_addr, 0, + (void *) qtd->hw_buf[0], + length, 0); +#if 0 + int i=0; + int *data_addr= qtd->hw_buf[0]; + printk("\n"); + for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++); + printk("\n"); +#endif + + + + break; + } + } + + /*unskip the tds at this location */ + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + skipmap &= ~td_ptd_map->ptd_bitmap; + lastmap = td_ptd_map->ptd_bitmap; + /*try to reduce the interrupts */ + ormask |= td_ptd_map->ptd_bitmap; + + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + break; + + case TD_PTD_BUFF_TYPE_INTL: + skipmap &= ~td_ptd_map->ptd_bitmap; + lastmap = td_ptd_map->ptd_bitmap; + intormask |= td_ptd_map->ptd_bitmap; + ; + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, + intormask); + break; + + case TD_PTD_BUFF_TYPE_ISTL: +#ifdef CONFIG_ISO_SUPPORT + iso_dbg(ISO_DBG_INFO, + "Never Error: Should not come here\n"); +#else + skipmap &= ~td_ptd_map->ptd_bitmap; + + isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, + skipmap); + + isp1763_reg_write16(hcd->dev, hcd->regs.isotdlastmap, + lasttd); +#endif /* CONFIG_ISO_SUPPORT */ + break; + } + + + } + /*if any new schedule, enable the atl buffer */ + + if (newschedule) { + switch (bufftype) { + case TD_PTD_BUFF_TYPE_ATL: + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | ATL_BUFFER); + /*i am comming here to only those tds that has to be scheduled */ + /*so skip map must be in place */ + if (skipmap & donemap) { + pehci_check + ("must be both ones compliment of each other\n"); + pehci_check + ("problem, skipmap %x, donemap %x,\n", + skipmap, donemap); + + } + skipmap &= ~donemap; + + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + + break; + case TD_PTD_BUFF_TYPE_INTL: + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | INT_BUFFER); + skipmap &= ~donemap; + + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + break; + case TD_PTD_BUFF_TYPE_ISTL: +#ifndef CONFIG_ISO_SUPPORT + + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus | ISO_BUFFER); +#endif + break; + } + } + spin_unlock(&hcd_data_lock); + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} +#endif + + + +static void +pehci_hcd_qtd_schedule(phci_hcd * hcd, struct ehci_qtd *qtd, + struct ehci_qh *qh, td_ptd_map_t * td_ptd_map) +{ + struct urb *urb; + urb_priv_t *urbpriv = 0; + u32 length=0; + struct isp1763_mem_addr *mem_addr = 0; + struct _isp1763_qha *qha, qhtemp; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + + if (qtd->state & QTD_STATE_SCHEDULED) { + return; + } + /*redundant */ + qha = &qhtemp; + + /*if this td is the last one */ + if (qtd->state & QTD_STATE_LAST) { + /*no qtd anymore */ + qh->hw_current = cpu_to_le32(0); + + /*else update the hw_next of qh to the next td */ + } else { + qh->hw_current = qtd->hw_next; + } + + urb = qtd->urb; + urbpriv = (urb_priv_t *) urb->hcpriv; + urbpriv->timeout = 0; + + /*NEW, now need to get the memory for this transfer */ + length = qtd->length; + mem_addr = &qtd->mem_addr; + phci_hcd_mem_alloc(length, mem_addr, 0); + if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) { + err("Never Error: Cannot allocate memory for the current td,length %d\n", length); + return; + } + + pehci_check("newqtd being scheduled, device: %d,map: %x\n", + urb->dev->devnum, td_ptd_map->ptd_bitmap); + + //udelay(100); + + memset(qha, 0, sizeof(isp1763_qha)); + /*convert qtd to qha */ + phci_hcd_qha_from_qtd(hcd, qtd, qtd->urb, (void *) qha, + td_ptd_map->ptd_ram_data_addr, qh + /*td_ptd_map->datatoggle */ ); + + if (qh->type == TD_PTD_BUFF_TYPE_INTL) { + phci_hcd_qhint_schedule(hcd, qh, qtd, (isp1763_qhint *) qha, + qtd->urb); + } + + + length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3); + if (length > HC_ATL_PL_SIZE) { + err("Never Error: Bogus length,length %d(max %d)\n", + qtd->length, HC_ATL_PL_SIZE); + } + + /*write qha into the header of the host controller */ + isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, 0); + +#if 0 //def PTD_DUMP_SCHEDULE + printk("SCHEDULE Next qtd\n"); + printk("DW0: 0x%08X\n", qha->td_info1); + printk("DW1: 0x%08X\n", qha->td_info2); + printk("DW2: 0x%08X\n", qha->td_info3); + printk("DW3: 0x%08X\n", qha->td_info4); +#endif + + /*if this is SETUP/OUT token , then need to write into the buffer */ + /*length should be valid */ + if (qtd->length && (length <= HC_ATL_PL_SIZE)){ + switch (PTD_PID(qha->td_info2)) { + case OUT_PID: + case SETUP_PID: + + isp1763_mem_write(hcd->dev, (u32) mem_addr->phy_addr, 0, + (void *) qtd->hw_buf[0], length, 0); + +#if 0 + int i=0; + int *data_addr= qtd->hw_buf[0]; + printk("\n"); + for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++); + printk("\n"); +#endif + + + break; + } + } + /*qtd is scheduled */ + qtd->state &= ~QTD_STATE_NEW; + qtd->state |= QTD_STATE_SCHEDULED; + + pehci_entry("-- %s: Exit\n", __FUNCTION__); + return; +} +#ifdef USBNET + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static void +pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map, struct pt_regs *regs) +#else +static void +pehci_hcd_urb_delayed_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map) +#endif +{ + static u32 remove = 0; + static u32 qh_state = 0; + + urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv; + +#ifdef USBNET + struct isp1763_async_cleanup_urb *urb_st = 0; +#endif + + + + urb_priv->timeout = 0; + + if((td_ptd_map->state == TD_PTD_REMOVE ) || + (urb_priv->state == DELETE_URB) || + !HCD_IS_RUNNING(hcd->state)){ + remove=1; + } + qh_state=qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + /*remove the done tds */ + spin_lock(&hcd_data_lock); + phci_hcd_urb_free_priv(hcd, urb_priv, qh); + spin_unlock(&hcd_data_lock); + + urb_priv->timeout = 0; + kfree(urb_priv); + urb->hcpriv = 0; + + + /*if normal completion */ + if (urb->status == -EINPROGRESS) { + urb->status = 0; + } + + if(remove) + if (list_empty(&qh->qtd_list)) { + phci_hcd_release_td_ptd_index(qh); + } + remove=0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0)) + usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb); +#endif + +//if(qh_state!=QH_STATE_COMPLETING) +{ +// spin_unlock(&hcd->lock); + /* assume interrupt has been disabled and has acquired hcd->lock */ + urb_st = (struct isp1763_async_cleanup_urb *)kmalloc(sizeof(struct isp1763_async_cleanup_urb), GFP_ATOMIC); + urb_st->urb = urb; + list_add_tail(&urb_st->urb_list, &(hcd->cleanup_urb.urb_list)); + +// isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, INTR_ENABLE_MASK | HC_SOF_INT); + isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, HC_MSOF_INT); +// spin_lock(&hcd->lock); +} + + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static void +pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map, struct pt_regs *regs) +#else +static void +pehci_hcd_urb_complete(phci_hcd * hcd, struct ehci_qh *qh, struct urb *urb, + td_ptd_map_t * td_ptd_map) +#endif +{ + static u32 remove = 0; + static u32 qh_state = 0; + urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv; + + BUG_ON(urb_priv==NULL); + + pehci_check("complete the td , length: %d\n", td_ptd_map->qtd->length); + urb_priv->timeout = 0; + + if((td_ptd_map->state == TD_PTD_REMOVE ) || + (urb_priv->state == DELETE_URB) || + !HCD_IS_RUNNING(hcd->state)){ + remove=1; + } + + + qh_state=qh->qh_state; + + qh->qh_state = QH_STATE_COMPLETING; + /*remove the done tds */ + spin_lock(&hcd_data_lock); + phci_hcd_urb_free_priv(hcd, urb_priv, qh); + spin_unlock(&hcd_data_lock); + + urb_priv->timeout = 0; + kfree(urb_priv); + urb->hcpriv = 0; + + + /*if normal completion */ + if (urb->status == -EINPROGRESS) { + urb->status = 0; + } + + if(remove) + if (list_empty(&qh->qtd_list)) { + phci_hcd_release_td_ptd_index(qh); + } + remove=0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0)) + { + usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd,urb); + } +#endif + spin_unlock(&hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(&hcd->usb_hcd, urb); +#else + usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status); +#endif + spin_lock(&hcd->lock); +exit: + pehci_entry("-- %s: Exit\n", __FUNCTION__); + +} + +/*update the error status of the td*/ +static void +pehci_hcd_update_error_status(u32 ptdstatus, struct urb *urb) +{ + /*if ptd status is halted */ + if (ptdstatus & PTD_STATUS_HALTED) { + if (ptdstatus & PTD_XACT_ERROR) { + /*transaction error results due to retry count goes to zero */ + if (PTD_RETRY(ptdstatus)) { + /*halt the endpoint */ + printk("transaction error , retries %d\n", + PTD_RETRY(ptdstatus)); + urb->status = -EPIPE; + } else { + printk("transaction error , retries %d\n", + PTD_RETRY(ptdstatus)); + /*protocol error */ + urb->status = -EPROTO; + } + } else if (ptdstatus & PTD_BABBLE) { + printk("babble error, qha %x\n", ptdstatus); + /*babble error */ + urb->status = -EOVERFLOW; + } else if (PTD_RETRY(ptdstatus)) { + printk("endpoint halted with retrie remaining %d\n", + PTD_RETRY(ptdstatus)); + urb->status = -EPIPE; + } else { /*unknown error, i will report it as halted, as i will never see xact error bit set */ + printk("protocol error, qha %x\n", ptdstatus); + urb->status = -EPIPE; + } + + /*if halted need to recover */ + if (urb->status == -EPIPE) { + } + } +} + +#ifdef CONFIG_ISO_SUPPORT /* New code for ISO support */ + +/******************************************************************* + * phcd_iso_handler - ISOCHRONOUS Transfer handler + * + * phci_hcd *hcd, + * Host controller driver structure which contains almost all data + * needed by the host controller driver to process data and interact + * with the host controller. + * + * struct pt_regs *regs + * + * API Description + * This is the ISOCHRONOUS Transfer handler, mainly responsible for: + * - Checking the periodic list if there are any ITDs for scheduling or + * removal. + * - For ITD scheduling, converting an ITD into a PTD, which is the data + * structure that the host contrtoller can understand and process. + * - For ITD completion, checking the transfer status and performing the + * required actions depending on status. + * - Freeing up memory used by an ITDs once it is not needed anymore. + ************************************************************************/ +void +pehci_hcd_iso_sitd_schedule(phci_hcd *hcd,struct urb* urb,struct ehci_sitd* sitd){ + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + struct _isp1763_isoptd *iso_ptd; + u32 ormask = 0, skip_map = 0,last_map=0,buff_stat=0; + struct isp1763_mem_addr *mem_addr; + ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]); + + /* Get the PTD allocated for this SITD. */ + td_ptd_map = + &ptd_map_buff->map_list[sitd-> + sitd_index]; + iso_ptd = &hcd->isotd; + + memset(iso_ptd, 0, sizeof(struct _isp1763_isoptd)); + /* Read buffer status register to check later if the ISO buffer is + filled or not */ + buff_stat = + isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat); + + /* Read the contents of the ISO skipmap register */ + skip_map = + isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, + skip_map); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_sitd_schedule]: Read skip map: 0x%08x\n", + (unsigned int) skip_map); + + /* Read the contents of the ISO lastmap register */ + last_map = + isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap, + last_map); + + /* Read the contents of the ISO ormask register */ + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or, + ormask); + + /* Create a PTD from an SITD */ + phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb, + (void *) iso_ptd); + /* Indicate that this SITD's PTD have been + filled up */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + + /* + * Place the newly initialized ISO PTD structure into + the location allocated for this PTD in the ISO PTD + memory region. + */ +#ifdef SWAP + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd, PHCI_QHA_LENGTH, 0, + PTD_HED); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd,PHCI_QHA_LENGTH, 0); +#endif + + /* + * Set this flag to avoid unlinking before + schedule at particular frame number + */ + td_ptd_map->state = TD_PTD_IN_SCHEDULE; + + /* + * If the length is not zero and the direction is + OUT then copy the data to be transferred + into the PAYLOAD memory area. + */ + if (sitd->length) { + switch (PTD_PID(iso_ptd->td_info2)) { + case OUT_PID: + /* Get the Payload memory + allocated for this PTD */ + mem_addr = &sitd->mem_addr; +#ifdef SWAP + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr-> phy_addr, + 0, (u32*) + ((sitd->hw_bufp[0])), + sitd->length, 0, + PTD_PAY); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr->phy_addr, + 0, (u32 *) + sitd->hw_bufp[0], + sitd->length, 0); +#endif + break; + } + /* switch(PTD_PID(iso_ptd->td_info2))*/ + } + + /* if(sitd->length) */ + /* If this is the last td, indicate to complete + the URB */ + if (sitd->hw_next == EHCI_LIST_END) { + td_ptd_map->lasttd = 1; + } + + /* + * Clear the bit corresponding to this PTD in + the skip map so that it will be processed on + the next schedule traversal. + */ + skip_map &= ~td_ptd_map->ptd_bitmap; + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_sitd_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map); + + /* + * Update the last map register to indicate + that the newly created PTD is the last PTD + added only if it is larger than the previous + bitmap. + */ + if (last_map < td_ptd_map->ptd_bitmap) { + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdlastmap, + td_ptd_map->ptd_bitmap); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_sitd_schedule]:Last Map: 0x%08x\n", + td_ptd_map->ptd_bitmap); + } + + /* + * Set the ISO_BUF_FILL bit to 1 to indicate + that there is a PTD for ISO that needs to + * be processed. + */ + isp1763_reg_write16(hcd->dev, + hcd->regs.buffer_status, + (buff_stat | ISO_BUFFER)); + + isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map); + +} + +/******************************************************************* + * phcd_iso_handler - ISOCHRONOUS Transfer handler + * + * phci_hcd *hcd, + * Host controller driver structure which contains almost all data + * needed by the host controller driver to process data and interact + * with the host controller. + * + * struct pt_regs *regs + * + * API Description + * This is the ISOCHRONOUS Transfer handler, mainly responsible for: + * - Checking the periodic list if there are any ITDs for scheduling or + * removal. + * - For ITD scheduling, converting an ITD into a PTD, which is the data + * structure that the host contrtoller can understand and process. + * - For ITD completion, checking the transfer status and performing the + * required actions depending on status. + * - Freeing up memory used by an ITDs once it is not needed anymore. + ************************************************************************/ +void +pehci_hcd_iso_schedule(phci_hcd * hcd, struct urb *urb) +{ + struct list_head *sitd_itd_sched, *position; + struct ehci_itd *itd; + struct ehci_sitd *sitd; + td_ptd_map_t *td_ptd_map; + unsigned long last_map; + td_ptd_map_buff_t *ptd_map_buff; + struct _isp1763_isoptd *iso_ptd; + unsigned long buff_stat; + struct isp1763_mem_addr *mem_addr; + u32 ormask = 0, skip_map = 0; + u32 iNumofPkts; + unsigned int iNumofSlots = 0, mult = 0; + struct ehci_qh *qhead; + + buff_stat = 0; + iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Enter\n"); + iso_ptd = &hcd->isotd; + + last_map = 0; + /* Check if there are any ITDs scheduled for processing */ + if (hcd->periodic_sched == 0) { + return; + } + if (urb->dev->speed == USB_SPEED_HIGH) { + mult = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + mult = 1 + ((mult >> 11) & 0x3); + iNumofSlots = NUMMICROFRAME / urb->interval; + /*number of PTDs need to schedule for this PTD */ + iNumofPkts = (urb->number_of_packets / mult) / iNumofSlots; + if ((urb->number_of_packets / mult) % iNumofSlots != 0){ + /*get remainder */ + iNumofPkts += 1; + } + } else{ + iNumofPkts = urb->number_of_packets; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qhead = urb->hcpriv; +#else + qhead = urb->ep->hcpriv; +#endif + if (!qhead) { + iso_dbg(ISO_DBG_ENTRY, + "[pehci_hcd_iso_schedule]: Qhead==NULL\n"); + return ; + } + ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]); + + while (iNumofPkts > 0) { + /* Read buffer status register to check later if the ISO buffer is + filled or not */ + buff_stat = + isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status,buff_stat); + + /* Read the contents of the ISO skipmap register */ + skip_map = + isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, + skip_map); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]: Read skip map: 0x%08x\n", + (unsigned int) skip_map); + + /* Read the contents of the ISO lastmap register */ + last_map = + isp1763_reg_read16(hcd->dev, hcd->regs.isotdlastmap, + last_map); + + /* Read the contents of the ISO ormask register */ + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or, + ormask); + + /* Process ITDs linked to this frame, checking if there are any that needs to + be scheduled */ + sitd_itd_sched = &qhead->periodic_list.sitd_itd_head; + if (list_empty(sitd_itd_sched)) { + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_schedule]: ISO schedule list's empty. Nothing to schedule.\n"); + return; + } + + list_for_each(position, sitd_itd_sched) { + if (qhead->periodic_list.high_speed == 0){ + /* Get an SITD in the list for processing */ + sitd = list_entry(position, struct ehci_sitd, + sitd_list); + iNumofPkts--; + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]: SITD Index:%d\n", sitd->sitd_index); + if(sitd->sitd_index==TD_PTD_INV_PTD_INDEX) + continue; + /* Get the PTD allocated for this SITD. */ + td_ptd_map = + &ptd_map_buff->map_list[sitd-> + sitd_index]; + memset(iso_ptd, 0, + sizeof(struct _isp1763_isoptd)); + + /* Create a PTD from an SITD */ + phcd_iso_sitd_to_ptd(hcd, sitd, sitd->urb, + (void *) iso_ptd); + + /* Indicate that this SITD's PTD have been + filled up */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + + /* + * Place the newly initialized ISO PTD structure into + the location allocated for this PTD in the ISO PTD + memory region. + */ +#ifdef SWAP + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd, PHCI_QHA_LENGTH, 0, + PTD_HED); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd,PHCI_QHA_LENGTH, 0); +#endif + + /* + * Set this flag to avoid unlinking before + schedule at particular frame number + */ + td_ptd_map->state = TD_PTD_IN_SCHEDULE; + + /* + * If the length is not zero and the direction is + OUT then copy the data to be transferred + into the PAYLOAD memory area. + */ + if (sitd->length) { + switch (PTD_PID(iso_ptd->td_info2)) { + case OUT_PID: + /* Get the Payload memory + allocated for this PTD */ + mem_addr = &sitd->mem_addr; +#ifdef SWAP + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr-> phy_addr, + 0, (u32*) + ((sitd->hw_bufp[0])), + sitd->length, 0, + PTD_PAY); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr->phy_addr, + 0, (u32 *) + sitd->hw_bufp[0], + sitd->length, 0); +#endif + break; + } + /* switch(PTD_PID(iso_ptd->td_info2))*/ + } + + /* if(sitd->length) */ + /* If this is the last td, indicate to complete + the URB */ + if (sitd->hw_next == EHCI_LIST_END) { + td_ptd_map->lasttd = 1; + } + + /* + * Clear the bit corresponding to this PTD in + the skip map so that it will be processed on + the next schedule traversal. + */ + skip_map &= ~td_ptd_map->ptd_bitmap; + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map); + + /* + * Update the last map register to indicate + that the newly created PTD is the last PTD + added only if it is larger than the previous + bitmap. + */ + if (last_map < td_ptd_map->ptd_bitmap) { + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdlastmap, + td_ptd_map->ptd_bitmap); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n", + td_ptd_map->ptd_bitmap); + } + + /* + * Set the ISO_BUF_FILL bit to 1 to indicate + that there is a PTD for ISO that needs to + * be processed. + */ + isp1763_reg_write16(hcd->dev, + hcd->regs.buffer_status, + (buff_stat | ISO_BUFFER)); + + } else { /*HIGH SPEED */ + + /* Get an ITD in the list for processing */ + itd = list_entry(position, struct ehci_itd, + itd_list); + iNumofPkts--; + + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]: ITD Index: %d\n", itd->itd_index); + /* Get the PTD allocated for this ITD. */ + td_ptd_map = + &ptd_map_buff->map_list[itd->itd_index]; + memset(iso_ptd, 0, + sizeof(struct _isp1763_isoptd)); + + /* Create a PTD from an ITD */ + phcd_iso_itd_to_ptd(hcd, itd, itd->urb, + (void *) iso_ptd); + + /* Indicate that this SITD's PTD have been + filled up */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + + /* + * Place the newly initialized ISO PTD + structure into the location allocated + * for this PTD in the ISO PTD memory region. + */ +#ifdef SWAP + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd,PHCI_QHA_LENGTH, 0, + PTD_HED); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) iso_ptd,PHCI_QHA_LENGTH, 0); +#endif + /* + * Set this flag to avoid unlinking before schedule + * at particular frame number + */ + td_ptd_map->state = TD_PTD_IN_SCHEDULE; + + /* + * If the length is not zero and the direction + is OUT then copy the data to be transferred + into the PAYLOAD memory area. + */ + if (itd->length) { + switch (PTD_PID(iso_ptd->td_info2)) { + case OUT_PID: + /* Get the Payload memory + allocated for this PTD */ + mem_addr = &itd->mem_addr; +#ifdef SWAP + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr->phy_addr, 0, + (u32*) + ((itd->hw_bufp[0])), + itd->length, 0, + PTD_PAY); +#else /* NO_SWAP */ + isp1763_mem_write(hcd->dev, + (unsigned long) + mem_addr->phy_addr, 0, + (u32 *)itd->hw_bufp[0], + itd->length, 0); +#endif + break; + } + /* switch(PTD_PID(iso_ptd->td_info2)) */ + } + + + /* If this is the last td, indicate to + complete the URB */ + if (itd->hw_next == EHCI_LIST_END) { + td_ptd_map->lasttd = 1; + } + + /* + * Clear the bit corresponding to this PT D + in the skip map so that it will be processed + on the next schedule traversal. + */ + skip_map &= ~td_ptd_map->ptd_bitmap; + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]: Skip map:0x%08x\n",(unsigned int) skip_map); + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdskipmap, + skip_map); + + /* + * Update the last map register to indicate + that the newly created PTD is the last PTD + added only if it is larger than the previous + bitmap. + */ + if (last_map < td_ptd_map->ptd_bitmap) { + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdlastmap, + td_ptd_map->ptd_bitmap); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_schedule]:Last Map: 0x%08x\n", + td_ptd_map->ptd_bitmap); + } + + /* + * Set the ISO_BUF_FILL bit to 1 to indicate + that there is a PTD for ISO that needs to + * be processed. + */ + isp1763_reg_write16(hcd->dev, + hcd->regs.buffer_status, + (buff_stat | ISO_BUFFER)); + } + } /* list_for_each(position, itd_sched) */ + isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap,skip_map); + }/*end of while (igNumOfPkts) */ + + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_schedule]: ISO-Frame scheduling done\n"); + iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_schedule]: Exit\n"); +} + +/******************************************************************* + * phcd_iso_handler - ISOCHRONOUS Transfer handler + * + * phci_hcd *hcd, + * Host controller driver structure which contains almost all data + * needed by the host controller driver to process data and interact + * with the host controller. + * + * struct pt_regs *regs + * + * API Description + * This is the ISOCHRONOUS Transfer handler, mainly responsible for: + * - Checking the periodic list if there are any ITDs for scheduling or + * removal. + * - For ITD scheduling, converting an ITD into a PTD, which is the data + * structure that the host contrtoller can understand and process. + * - For ITD completion, checking the transfer status and performing the + * required actions depending on status. + * - Freeing up memory used by an ITDs once it is not needed anymore. + ************************************************************************/ + +int debugiso = 0; + +void +pehci_hcd_iso_worker(phci_hcd * hcd) +{ + u32 donemap = 0, skipmap = 0; /*ormask = 0, buff_stat = 0;*/ + u32 pendingmap = 0; + u32 mask = 0x1, index = 0, donetoclear = 0; + u32 uFrIndex = 0; + unsigned char last_td = FALSE, iReject = 0; + struct isp1763_mem_addr *mem_addr; + struct _isp1763_isoptd *iso_ptd; + unsigned long length = 0, uframe_cnt, usof_stat; + struct ehci_qh *qhead; + struct ehci_itd *itd, *current_itd; + struct ehci_sitd *sitd=0, *current_sitd=0; + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + struct list_head *sitd_itd_remove, *position;// *lst_temp; + struct urb *urb; + u8 i = 0; + unsigned long startAdd = 0; + int ret = 0; + + + iso_ptd = &hcd->isotd; + + /* Check if there are any ITDs scheduled for processing */ + if (hcd->periodic_sched == 0) { + goto exit; + } + ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]); + pendingmap = ptd_map_buff->pending_ptd_bitmap; + + + /*read the done map for interrupt transfers */ + donemap = isp1763_reg_read16(hcd->dev, hcd->regs.isotddonemap, donemap); + + iso_dbg(ISO_DBG_ENTRY, "[pehci_hcd_iso_worker]: Enter %x \n", donemap); + if (!donemap) { /*there isnt any completed PTD */ + goto exit; + } + donetoclear = donemap; + uFrIndex = 0; + while (donetoclear) { + mask = 0x1 << uFrIndex; + index = uFrIndex; + uFrIndex++; + if (!(donetoclear & mask)) + continue; + donetoclear &= ~mask; + iso_dbg(ISO_DBG_DATA, "[pehci_hcd_iso_worker]: uFrIndex = %d\n", index); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]:donetoclear = 0x%x mask = 0x%x\n", + donetoclear, mask); + + + if (ptd_map_buff->map_list[index].sitd) { + urb = ptd_map_buff->map_list[index].sitd->urb; + if (!urb) { + printk("ERROR : URB is NULL \n"); + continue; + } + sitd = ptd_map_buff->map_list[index].sitd; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qhead=urb->hcpriv; +#else + qhead = urb->ep->hcpriv; +#endif + if (!qhead) { + printk("ERROR : Qhead is NULL \n"); + continue; + } + + sitd_itd_remove = &qhead->periodic_list.sitd_itd_head; + } else if (ptd_map_buff->map_list[index].itd) { + urb = ptd_map_buff->map_list[index].itd->urb; + if (!urb) { + printk("ERROR : URB is NULL \n"); + continue; + } + itd = ptd_map_buff->map_list[index].itd; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qhead=urb->hcpriv; +#else + qhead = urb->ep->hcpriv; +#endif + if (!qhead) { + printk("ERROR : Qhead is NULL \n"); + continue; + } + + sitd_itd_remove = &qhead->periodic_list.sitd_itd_head; + + } else { + printk("ERROR : NO sitd in that PTD location : \n"); + continue; + } + /* Process ITDs linked to this frame, checking for completed ITDs */ + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: Removal Frame number: %d\n", + (int) index); + if (list_empty(sitd_itd_remove)) { + continue; + } + + if (urb) { + last_td = FALSE; + if (qhead->periodic_list.high_speed == 0)/*FULL SPEED*/ + { + + /* Get the PTD that was allocated for this + particular SITD*/ + td_ptd_map = + &ptd_map_buff->map_list[sitd-> + sitd_index]; + + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker]: PTD is done,%d\n",index); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: SITD Index: %d\n",sitd->sitd_index); + urb = sitd->urb; + + /* + * Get the base address of the memory allocated + in the PAYLOAD region for this SITD + */ + mem_addr = &sitd->mem_addr; + memset(iso_ptd, 0, + sizeof(struct _isp1763_isoptd)); + + /* + * Read this ptd from the ram address, + address is in the td_ptd_map->ptd_header_addr + */ + + isp1763_mem_read(hcd->dev, + td_ptd_map->ptd_header_addr, + 0, (u32 *) iso_ptd, + PHCI_QHA_LENGTH, 0); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD0 = 0x%08x\n", iso_ptd->td_info1); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD1 = 0x%08x\n", iso_ptd->td_info2); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD2 = 0x%08x\n", iso_ptd->td_info3); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD3 = 0x%08x\n", iso_ptd->td_info4); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD4 = 0x%08x\n", iso_ptd->td_info5); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD5 = 0x%08x\n", iso_ptd->td_info6); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD6 = 0x%08x\n", iso_ptd->td_info7); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD7 = 0x%08x\n", iso_ptd->td_info8); + + /* Go over the status of each of the 8 Micro Frames */ + for (uframe_cnt = 0; uframe_cnt < 8; + uframe_cnt++) { + /* + * We go over the status one at a time. The status bits and their + * equivalent status are: + * Bit 0 - Transaction Error (IN and OUT) + * Bit 1 - Babble (IN token only) + * Bit 2 - Underrun (OUT token only) + */ + usof_stat = + iso_ptd->td_info5 >> (8 + + (uframe_cnt * 3)); + + switch (usof_stat & 0x7) { + case INT_UNDERRUN: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Buffer underrun\n"); + urb->error_count++; + break; + case INT_EXACT: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Transaction error\n"); + printk("[pehci_hcd_iso_worker Error]: Transaction error\n"); + urb->error_count++; + break; + case INT_BABBLE: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Babble error\n"); + printk("[pehci_hcd_iso_worker Error]: Babble error\n"); + urb->iso_frame_desc[sitd->sitd_index].status + = -EOVERFLOW; + urb->error_count++; + break; + } /* switch(usof_stat & 0x7) */ + } /* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */ + + /* + * Get the number of bytes transferred. This indicates the number of + * bytes sent or received for this transaction. + */ + if (urb->dev->speed != USB_SPEED_HIGH) { + /* Length is 1K for full/low speed device */ + length = PTD_XFERRED_NONHSLENGTH + (iso_ptd->td_info4); + } else { + /* Length is 32K for high speed device */ + length = PTD_XFERRED_LENGTH(iso_ptd-> + td_info4); + } + + /* Halted, need to finish all the transfer on this endpoint */ + if (iso_ptd->td_info4 & PTD_STATUS_HALTED) { + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error] PTD Halted\n"); + printk("[pehci_hcd_iso_worker Error] PTD Halted\n"); + /* + * When there is an error, do not process the other PTDs. + * Stop at the PTD with the error and remove all other PTDs. + */ + td_ptd_map->lasttd = 1; + + /* + * In case of halt, next transfer will start with toggle zero, + * USB specs, 5.8.5 + */ + td_ptd_map->datatoggle = 0; + } + + /* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */ + /* Update the actual length of the transfer from the data we got earlier */ + urb->iso_frame_desc[sitd->index].actual_length = + length; + + /* If the PTD have been executed properly the V bit should be cleared */ + if (iso_ptd->td_info1 & QHA_VALID) { + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Valid bit not cleared\n"); + printk("[pehci_hcd_iso_worker Error]: Valid bit not cleared\n"); + urb->iso_frame_desc[sitd->index]. + status = -ENOSPC; + } else { + urb->iso_frame_desc[sitd->index]. + status = 0; + } + + /* Check if this is the last SITD either due to some error or normal completion */ + if ((td_ptd_map->lasttd) + || (sitd->hw_next == EHCI_LIST_END)) { + last_td = TRUE; + } + + /* Copy data to/from */ + if (length && (length <= MAX_PTD_BUFFER_SIZE)) { + switch (PTD_PID(iso_ptd->td_info2)) { + case IN_PID: + /* + * Get the data from the PAYLOAD area and place it into + * the buffer provided by the requestor. + */ + + isp1763_mem_read(hcd->dev, + (unsigned long)mem_addr-> + phy_addr, 0,(u32 *) + sitd->hw_bufp[0], + length, 0); + + case OUT_PID: + /* + * urb->actual length was initialized to zero, so for the first + * uFrame having it incremented immediately is not a problem. + */ + urb->actual_length += length; + break; + }/* switch(PTD_PID(iso_ptd->td_info2)) */ + } + /* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */ +// removesitd: + /*read skip-map */ + skipmap = + isp1763_reg_read16(hcd->dev, + hcd->regs.isotdskipmap, + skipmap); + iso_dbg(ISO_DBG_DATA, + "[%s] : read skipmap =0x%x\n", + __FUNCTION__, skipmap); + if (last_td == TRUE) { + /* Start removing the ITDs in the list */ + while (1) { + /* + * This indicates that we are processing the tail PTD. + * Perform cleanup procedure on this last PTD + */ + if (sitd->hw_next == EHCI_LIST_END) { + td_ptd_map = + &ptd_map_buff-> + map_list[sitd-> + sitd_index]; + + /* + * Free up our allocation in the PAYLOAD area so that others can use + * it. + */ +#ifndef COMMON_MEMORY + phci_hcd_mem_free + (&sitd-> + mem_addr); +#endif + /* Remove this SITD entry in the SITD list */ + list_del(&sitd-> + sitd_list); + + /* Free up the memory allocated for the SITD structure */ + qha_free(qha_cache, + sitd); + + /* Indicate that the PTD we have used is now free */ + td_ptd_map->state = + TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + + /* Decrease the number of active PTDs scheduled */ + hcd->periodic_sched--; + + /* Skip this PTD during the next PTD processing. */ + skipmap |= + td_ptd_map->ptd_bitmap; + isp1763_reg_write16 + (hcd->dev, + hcd->regs. + isotdskipmap, + skipmap); + + /* All ITDs in this list have been successfully removed. */ + break; + } else { + /* + * This indicates that we stopped due to an error on a PTD that is + * not the last in the list. We need to free up this PTD as well as + * the PTDs after it. + */ + /* + * Put the current SITD error onto this variable. + * We will be unlinking this from the list and free up its + * resources later. + */ + current_sitd = sitd; + + td_ptd_map = + &ptd_map_buff-> + map_list[sitd-> + sitd_index]; + + /* + * Get the next SITD, and place it to the sitd variable. + * In a way we are moving forward in the SITD list. + */ + sitd = (struct ehci_sitd + *) + (current_sitd-> + hw_next); + /* Free up the current SITD's resources */ +#ifndef COMMON_MEMORY + phci_hcd_mem_free + (¤t_sitd-> + mem_addr); +#endif + /* Remove this SITD entry in the SITD list */ + list_del(¤t_sitd-> + sitd_list); + + /* Free up the memory allocated for the SITD structure */ + qha_free(qha_cache, + current_sitd); + + /* Inidicate that the PTD we have used is now free */ + td_ptd_map->state = + TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + + /* Decrease the number of active PTDs scheduled */ + hcd->periodic_sched--; + + /* Sine it is done, skip this PTD during the next PTD processing. */ + skipmap |= + td_ptd_map-> + ptd_bitmap; + isp1763_reg_write16 + (hcd->dev, + hcd->regs. + isotdskipmap, + skipmap); + /* + * Start all over again until it gets to the tail of the + * list of PTDs/ITDs + */ + continue; + } /* else of if(sitd->hw_next == EHCI_LIST_END) */ + + /* It should never get here, but I put this as a precaution */ + break; + } /*end of while(1) */ + + /* Check if there were ITDs that were not processed due to the error */ + if (urb->status == -EINPROGRESS) { + if ((urb->actual_length != + urb->transfer_buffer_length) + && (urb->transfer_flags & + URB_SHORT_NOT_OK)) { + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Short Packet\n"); + urb->status = + -EREMOTEIO; + } else { + urb->status = 0; + } + } + + urb->hcpriv = 0; + iso_dbg(ISO_DBG_DATA, + "[%s] : remain skipmap =0x%x\n", + __FUNCTION__, skipmap); +#ifdef COMMON_MEMORY + phci_hcd_mem_free(&qhead->memory_addr); +#endif + /* We need to unlock this here, since this was locked when we are called + * from the interrupt handler */ + spin_unlock(&hcd->lock); + /* Perform URB cleanup */ + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker] Complete a URB\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0)) + usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, + urb); +#endif + hcd->periodic_more_urb = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qhead=urb->hcpriv; + if (!list_empty(&qhead->ep->urb_list)) +#else + if (!list_empty(&urb->ep->urb_list)) +#endif + hcd->periodic_more_urb = 1; + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(&hcd->usb_hcd, urb); +#else + usb_hcd_giveback_urb(&hcd->usb_hcd, urb, urb->status); +#endif + + spin_lock(&hcd->lock); + continue; + } + + /* if( last_td == TRUE ) */ + /* + * If the last_td is not set then we do not need to check for errors and directly + * proceed with the cleaning sequence. + */ + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker]: last_td is not set\n"); + /*update skipmap */ + skipmap |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdskipmap, + skipmap); + iso_dbg(ISO_DBG_DATA, + "%s : remain skipmap =0x%x\n", + __FUNCTION__, skipmap); + + /* Decrement the count of active PTDs */ + hcd->periodic_sched--; + /*schedule next PTD for this URB */ + if(qhead->actualptds<qhead->totalptds) + { + sitd_itd_remove = &qhead->periodic_list.sitd_itd_head; + /* find sitd to schedule */ + list_for_each(position, sitd_itd_remove) { + + if (qhead->periodic_list.high_speed == 0){ + /* Get an SITD in the list for processing */ + current_sitd= list_entry(position, struct ehci_sitd, + sitd_list); + if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX) + break; + } + } + if(current_sitd->sitd_index==TD_PTD_INV_PTD_INDEX){ + qhead->actualptds++; + /*allocate memory and PTD index */ + memcpy(¤t_sitd->mem_addr,&sitd->mem_addr,sizeof(struct isp1763_mem_addr)); +// printk("current %x\n",sitd->sitd_index); + current_sitd->sitd_index=sitd->sitd_index; + /*schedule PTD */ + td_ptd_map->sitd = current_sitd; + hcd->periodic_sched++; + pehci_hcd_iso_sitd_schedule(hcd, urb,current_sitd); + } + + /* Remove this SITD from the list of active ITDs */ + list_del(&sitd->sitd_list); + + /* Free up the memory we allocated for the SITD structure */ + qha_free(qha_cache, sitd); + + + }else{ +#ifndef COMMON_MEMORY + phci_hcd_mem_free(&sitd->mem_addr); +#endif + /* Remove this SITD from the list of active ITDs */ + list_del(&sitd->sitd_list); + + /* Free up the memory we allocated for the SITD structure */ + qha_free(qha_cache, sitd); + + /* + * Clear the bit associated with this PTD from the grouptdmap and + * make this PTD available for other transfers + */ + td_ptd_map->state = TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + + } + + + + } else { /*HIGH SPEED */ + + /* Get an ITD in the list for processing */ + itd = ptd_map_buff->map_list[index].itd; + + /* Get the PTD that was allocated for this particular ITD. */ + td_ptd_map = + &ptd_map_buff->map_list[itd->itd_index]; + + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker]: PTD is done , %d\n", + index); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: ITD Index: %d\n", + itd->itd_index); + + urb = itd->urb; + + /* + * Get the base address of the memory allocated in the + * PAYLOAD region for this ITD + */ + mem_addr = &itd->mem_addr; + memset(iso_ptd, 0, + sizeof(struct _isp1763_isoptd)); + + /* + * Read this ptd from the ram address,address is in the + * td_ptd_map->ptd_header_addr + */ + + isp1763_mem_read(hcd->dev, + td_ptd_map->ptd_header_addr, + 0, (u32 *) iso_ptd, + PHCI_QHA_LENGTH, 0); + + /* + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD0 = + 0x%08x\n", iso_ptd->td_info1); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD1 = + 0x%08x\n", iso_ptd->td_info2); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD2 = + 0x%08x\n", iso_ptd->td_info3); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD3 = + 0x%08x\n", iso_ptd->td_info4); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD4 = + 0x%08x\n",iso_ptd->td_info5); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD5 = + 0x%08x\n", iso_ptd->td_info6); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD6 = + 0x%08x\n", iso_ptd->td_info7); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_iso_worker]: DWORD7 = + 0x%08x\n", iso_ptd->td_info8); + */ + + + /* If the PTD have been executed properly, + the V bit should be cleared */ + if (iso_ptd->td_info1 & QHA_VALID) { + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Valid bit not cleared\n"); + for(i = 0; i<itd->num_of_pkts; i++){ + urb->iso_frame_desc[itd->index + + i].status = -ENOSPC; + } + } else { + for (i = 0; i<itd->num_of_pkts; i++){ + urb->iso_frame_desc[itd->index + +i].status = 0; + } + } + + /* Go over the status of each of the 8 Micro Frames */ + for (uframe_cnt = 0; (uframe_cnt < 8) + && (uframe_cnt < itd->num_of_pkts); + uframe_cnt++) { + /* + * We go over the status one at a time. The status bits and their + * equivalent status are: + * Bit 0 - Transaction Error (IN and OUT) + * Bit 1 - Babble (IN token only) + * Bit 2 - Underrun (OUT token only) + */ + usof_stat = + iso_ptd->td_info5 >> (8 + + (uframe_cnt * 3)); + + switch (usof_stat & 0x7) { + case INT_UNDERRUN: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Buffer underrun\n"); + urb->iso_frame_desc[itd->index + + uframe_cnt]. + status = -ECOMM; + urb->error_count++; + break; + case INT_EXACT: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: %p Transaction error\n", + urb); + urb->iso_frame_desc[itd->index + + uframe_cnt]. + status = -EPROTO; + urb->error_count++; + debugiso = 25; + break; + case INT_BABBLE: + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Babble error\n"); + urb->iso_frame_desc[itd->index + + uframe_cnt]. + status = -EOVERFLOW; + urb->error_count++; + break; + }/* switch(usof_stat & 0x7) */ + }/* end of for( ulMicroFrmCnt = 0; ulMicroFrmCnt < 8; ulMicroFrmCnt++) */ + + /* + * Get the number of bytes transferred. This indicates the number of + * bytes sent or received for this transaction. + */ + + /* Length is 32K for high speed device */ + length = PTD_XFERRED_LENGTH(iso_ptd->td_info4); + + /* Halted, need to finish all the transfer on this endpoint */ + if (iso_ptd->td_info4 & PTD_STATUS_HALTED) { + + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error] PTD Halted\n"); + printk("[pehci_hcd_iso_worker Error] PTD Halted===============\n"); + /* + * When there is an error, do not process the other PTDs. + * Stop at the PTD with the error and remove all other PTDs. + */ + td_ptd_map->lasttd = 1; + + /* + * In case of halt, next transfer will start with toggle zero, + * USB specs, 5.8.5 + */ + td_ptd_map->datatoggle = 0; + } + /* if(iso_ptd->td_info4 & PTD_STATUS_HALTED) */ + /* Update the actual length of the transfer from the data we got earlier */ + if (PTD_PID(iso_ptd->td_info2) == OUT_PID) { + for (i = 0; i < itd->num_of_pkts; i++){ + urb->iso_frame_desc[itd->index + + i].actual_length =(unsigned int) + length / itd->num_of_pkts; + } + } else{ + iso_dbg(ISO_DBG_DATA, + "itd->num_of_pkts = %d, itd->ssplit = %x\n", + itd->num_of_pkts, itd->ssplit); + urb->iso_frame_desc[itd->index + + 0].actual_length = + iso_ptd->td_info6 & 0x00000FFF; + iso_dbg(ISO_DBG_DATA, + "actual length[0] = %d\n", + urb->iso_frame_desc[itd->index +0]. + actual_length); + + if((itd->num_of_pkts > 1) + && ((itd->ssplit & 0x2) == 0x2) + && (urb->iso_frame_desc[itd->index + + 1].status ==0)) { + + urb->iso_frame_desc[itd->index +1]. + actual_length = (iso_ptd-> + td_info6 & 0x00FFF000)>> 12; + + iso_dbg(ISO_DBG_DATA, + "actual length[1] = %d\n", + urb-> + iso_frame_desc[itd-> + index + 1]. + actual_length); + }else{ + urb->iso_frame_desc[itd->index +1]. + actual_length = 0; + } + + if ((itd->num_of_pkts > 2) + && ((itd->ssplit & 0x4) == 0x4) + && (urb-> + iso_frame_desc[itd->index + + 2].status ==0)) { + + urb->iso_frame_desc[itd->index + + 2].actual_length = + ((iso_ptd->td_info6 & + 0xFF000000 )>> 24) + | ((iso_ptd->td_info7 + & 0x0000000F)<< 8); + + iso_dbg(ISO_DBG_DATA, + "actual length[2] = %d\n", + urb->iso_frame_desc[itd-> + index + 2].actual_length); + } else{ + urb->iso_frame_desc[itd->index +2]. + actual_length = 0; + } + + if ((itd->num_of_pkts > 3) + && ((itd->ssplit & 0x8) == 0x8) + && (urb->iso_frame_desc[itd->index + + 3].status == 0)) { + + urb->iso_frame_desc[itd->index + 3]. + actual_length =(iso_ptd-> + td_info7 & 0x0000FFF0)>> 4; + + iso_dbg(ISO_DBG_DATA, + "actual length[3] = %d\n", + urb->iso_frame_desc[itd-> + index + 3].actual_length); + } else { + urb->iso_frame_desc[itd->index +3]. + actual_length = 0; + } + + if ((itd->num_of_pkts > 4) + && ((itd->ssplit & 0x10) == 0x10) + && (urb-> + iso_frame_desc[itd->index + + 4].status ==0)) { + + urb->iso_frame_desc[itd->index + + 4].actual_length = + (iso_ptd-> + td_info7 & 0x0FFF0000) >> 16; + + iso_dbg(ISO_DBG_DATA, + "actual length[4] = %d\n", + urb->iso_frame_desc[itd->index + + 4].actual_length); + } else { + urb->iso_frame_desc[itd->index + + 4].actual_length = 0; + } + + if ((itd->num_of_pkts > 5) + && ((itd->ssplit & 0x20) == 0x20) + && (urb-> + iso_frame_desc[itd->index + + 5].status == + 0)) { + + urb->iso_frame_desc[itd->index + + 5].actual_length = + ((iso_ptd-> + td_info7 & 0xF0000000) >> 28) | + ((iso_ptd->td_info8 & + 0x000000FF) + << 4); + + iso_dbg(ISO_DBG_DATA, + "actual length[5] = %d\n", + urb-> + iso_frame_desc[itd-> + index + + 5].actual_length); + } else { + urb->iso_frame_desc[itd->index + + 5].actual_length = 0; + } + + if ((itd->num_of_pkts > 6) + && ((itd->ssplit & 0x40) == 0x40) + && (urb-> + iso_frame_desc[itd->index + + 6].status ==0)) { + + urb->iso_frame_desc[itd->index + + 6].actual_length = + (iso_ptd-> + td_info8 & 0x000FFF00) + >> 8; + + iso_dbg(ISO_DBG_DATA, + "actual length[6] = %d\n", + urb-> + iso_frame_desc[itd-> + index + + 6].actual_length); + } else { + urb->iso_frame_desc[itd->index + + 6].actual_length = 0; + } + + if ((itd->num_of_pkts > 7) + && ((itd->ssplit & 0x80) == 0x80) + && (urb-> + iso_frame_desc[itd->index + + 7].status == + 0)) { + + urb->iso_frame_desc[itd->index + + 7].actual_length = + (iso_ptd-> + td_info8 & 0xFFF00000) >> 20; + + iso_dbg(ISO_DBG_DATA, + "actual length[7] = %d\n", + urb-> + iso_frame_desc[itd-> + index + + 7].actual_length); + } else { + urb->iso_frame_desc[itd->index + + 7].actual_length = 0; + } + } + /* Check if this is the last ITD either due to some error or normal completion */ + if ((td_ptd_map->lasttd) + || (itd->hw_next == EHCI_LIST_END)) { + + last_td = TRUE; + + } + + /* Copy data to/from */ + if (length && (length <= MAX_PTD_BUFFER_SIZE)) { + switch (PTD_PID(iso_ptd->td_info2)) { + case IN_PID: + /* + * Get the data from the PAYLOAD area and place it into + * the buffer provided by the requestor. + */ + /*for first packet*/ + startAdd = mem_addr->phy_addr; + iso_dbg(ISO_DBG_DATA, + "start add = %ld hw_bufp[0] = 0x%08x length = %d\n", + startAdd, + itd->hw_bufp[0], + urb-> + iso_frame_desc[itd-> + index].actual_length); + if (urb-> + iso_frame_desc[itd->index]. + status == 0) { + + if (itd->hw_bufp[0] ==0) { + dma_addr_t + buff_dma; + + buff_dma = + (u32) ((unsigned char *) urb->transfer_buffer + + urb->iso_frame_desc[itd->index].offset); + itd->buf_dma = + buff_dma; + itd->hw_bufp[0] + = + buff_dma; + } + if (itd->hw_bufp[0] !=0) { + + ret = isp1763_mem_read(hcd->dev, (unsigned long) + startAdd, + 0,(u32*)itd-> + hw_bufp[0], + urb-> + iso_frame_desc + [itd-> + index]. + actual_length, + 0); + + } else { + printk("isp1763_mem_read data payload fail\n"); + printk("start add = %ld hw_bufp[0] = 0x%08x length = %d\n", + startAdd, itd->hw_bufp[0], + urb->iso_frame_desc[itd->index].actual_length); + urb->iso_frame_desc[itd->index].status = -EPROTO; + urb->error_count++; + } + } + + + for (i = 1; + i < itd->num_of_pkts; + i++) { + startAdd += + (unsigned + long) (urb-> + iso_frame_desc + [itd-> + index + + i - 1]. + actual_length); + + iso_dbg(ISO_DBG_DATA, + "start add = %ld hw_bufp[%d] = 0x%08x length = %d\n", + startAdd, i, + itd->hw_bufp[i], + urb-> + iso_frame_desc + [itd->index + + i]. + actual_length); + if (urb-> + iso_frame_desc[itd-> + index + i]. + status == 0) { + + isp1763_mem_read + (hcd->dev, + startAdd, + 0,(u32*) + itd-> + hw_bufp + [i],urb-> + iso_frame_desc + [itd-> + index + i]. + actual_length, + 0); + + if (ret == -EINVAL){ + printk("isp1763_mem_read data payload fail %d\n", i); + } + } + } + + case OUT_PID: + /* + * urb->actual length was initialized to zero, so for the first + * uFrame having it incremented immediately is not a problem. + */ + urb->actual_length += length; + break; + } /* switch(PTD_PID(iso_ptd->td_info2)) */ + } + + /* if(length && (length <= MAX_PTD_BUFFER_SIZE)) */ +// removeitd: + /*read skip-map */ + skipmap = + isp1763_reg_read16(hcd->dev, + hcd->regs.isotdskipmap, + skipmap); + + iso_dbg(ISO_DBG_DATA, + "[%s] : read skipmap =0x%x\n", + __FUNCTION__, skipmap); + if (last_td == TRUE) { + /* Start removing the ITDs in the list */ + while (1) { + /* + * This indicates that we are processing the tail PTD. + * Perform cleanup procedure on this last PTD + */ + if (itd->hw_next == + EHCI_LIST_END) { + td_ptd_map = + &ptd_map_buff-> + map_list[itd-> + itd_index]; + + /* + * Free up our allocation in the PAYLOAD area so that others can use + * it. + */ +#ifndef COMMON_MEMORY + phci_hcd_mem_free(&itd-> + mem_addr); +#endif + + /* Remove this ITD entry in the ITD list */ + list_del(&itd-> + itd_list); + + /* Free up the memory allocated for the ITD structure */ + qha_free(qha_cache, + itd); + + /* Indicate that the PTD we have used is now free */ + td_ptd_map->state = + TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + + /* Decrease the number of active PTDs scheduled */ + hcd->periodic_sched--; + + /* Skip this PTD during the next PTD processing. */ + skipmap |= + td_ptd_map-> + ptd_bitmap; + + isp1763_reg_write16 + (hcd->dev, + hcd->regs. + isotdskipmap, + skipmap); + + /* All ITDs in this list have been successfully removed. */ + break; + } + /* if(itd->hw_next == EHCI_LIST_END) */ + /* + * This indicates that we stopped due to an error on a PTD that is + * not the last in the list. We need to free up this PTD as well as + * the PTDs after it. + */ + else { + /* + * Put the current ITD error onto this variable. + * We will be unlinking this from the list and free up its + * resources later. + */ + current_itd = itd; + + td_ptd_map = + &ptd_map_buff-> + map_list[itd-> + itd_index]; + + /* + * Get the next ITD, and place it to the itd variable. + * In a way we are moving forward in the ITD list. + */ + itd = (struct ehci_itd + *) (current_itd-> + hw_next); +#ifndef COMMON_MEMORY + /* Free up the current ITD's resources */ + phci_hcd_mem_free + (¤t_itd-> + mem_addr); +#endif + + /* Remove this ITD entry in the ITD list */ + list_del(¤t_itd-> + itd_list); + + /* Free up the memory allocated for the ITD structure */ + qha_free(qha_cache, + current_itd); + + /* Inidicate that the PTD we have used is now free */ + td_ptd_map->state = + TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + + /* Decrease the number of active PTDs scheduled */ + hcd->periodic_sched--; + + /* Sine it is done, skip this PTD during the next PTD processing. */ + skipmap |= + td_ptd_map-> + ptd_bitmap; + isp1763_reg_write16 + (hcd->dev, + hcd->regs. + isotdskipmap, + skipmap); + /* + * Start all over again until it gets to the tail of the + * list of PTDs/ITDs + */ + continue; + }/* else of if(itd->hw_next == EHCI_LIST_END) */ + /* It should never get here, but I put this as a precaution */ + break; + } /*end of while(1) */ + /* Check if there were ITDs that were not processed due to the error */ + if (urb->status == -EINPROGRESS) { + if ((urb->actual_length != + urb->transfer_buffer_length) + && (urb-> + transfer_flags & + URB_SHORT_NOT_OK)) { + + iso_dbg(ISO_DBG_ERR, + "[pehci_hcd_iso_worker Error]: Short Packet\n"); + + urb->status = + -EREMOTEIO; + } else { + urb->status = 0; + } + } + + urb->hcpriv = 0; + iso_dbg(ISO_DBG_DATA, + "[%s] : remain skipmap =0x%x\n", + __FUNCTION__, skipmap); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) +// if (urb->reject.counter) { + if (unlikely(atomic_read(&urb->reject))) {// kernel reference code hcd.c + iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__); + iReject = 1; + } +#else + if (unlikely(urb->reject)) { + iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__); + iReject = 1; + } +#endif + +/* +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) + + if (urb->reject.counter) { + iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__); + iReject = 1; + } +#else + if (unlikely(urb->reject)) { + + + iso_dbg("ISO_DBG_INFO, [%s] urb reject\n", __FUNCTION__); + iReject = 1; + } +#endif +*/ + +#ifdef COMMON_MEMORY + phci_hcd_mem_free(&qhead->memory_addr); +#endif + /* We need to unlock this here, since this was locked when we are called */ + /* from the interrupt handler */ + spin_unlock(&hcd->lock); + /* Perform URB cleanup */ + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker] Complete a URB\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0)) + usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb); +#endif + hcd->periodic_more_urb = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qhead=urb->hcpriv; + if (!list_empty(&qhead->ep->urb_list)){ + +#else + if (!list_empty(&urb->ep->urb_list)){ +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + if (urb->hcpriv== periodic_ep[0]){ +#else + if (urb->ep == periodic_ep[0]){ +#endif + hcd->periodic_more_urb = + 1; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + } else if (urb->hcpriv== + periodic_ep[1]){ +#else + } else if (urb->ep == + periodic_ep[1]){ +#endif + hcd->periodic_more_urb = + 2; + } else { + hcd->periodic_more_urb = + 0; + } + + + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(&hcd->usb_hcd, urb); +#else + usb_hcd_giveback_urb(&hcd->usb_hcd, urb, + urb->status); +#endif + + spin_lock(&hcd->lock); + continue; + } + /* if( last_td == TRUE ) */ + /* + * If the last_td is not set then we do not need to check for errors and directly + * proceed with the cleaning sequence. + */ + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker]: last_td is not set\n"); + /*update skipmap */ + skipmap |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, + hcd->regs.isotdskipmap, + skipmap); + iso_dbg(ISO_DBG_DATA, + "%s : remain skipmap =0x%x\n", + __FUNCTION__, skipmap); + + /* Decrement the count of active PTDs */ + hcd->periodic_sched--; +#ifndef COMMON_MEMORY + /* Free up the memory we allocated in the PAYLOAD area */ + phci_hcd_mem_free(&itd->mem_addr); +#endif + /* Remove this ITD from the list of active ITDs */ + list_del(&itd->itd_list); + + /* Free up the memory we allocated for the ITD structure */ + qha_free(qha_cache, itd); + /* + * Clear the bit associated with this PTD from the grouptdmap and + * make this PTD available for other transfers + */ + td_ptd_map->state = TD_PTD_NEW; + td_ptd_map->sitd = NULL; + td_ptd_map->itd = NULL; + } /*end of HIGH SPEED */ + } /* end of list_for_each_safe(position, lst_temp, itd_remove) */ + iso_dbg(ISO_DBG_INFO, + "[pehci_hcd_iso_worker]: ISO-Frame removal done\n"); + + + } /* while donetoclear */ + + + if (iReject) { + spin_unlock(&hcd->lock); + if (hcd->periodic_more_urb) { + + if(periodic_ep[hcd->periodic_more_urb]) + while (&periodic_ep[hcd->periodic_more_urb - 1]-> + urb_list) { + + urb = container_of(periodic_ep + [hcd->periodic_more_urb - + 1]->urb_list.next, + struct urb, urb_list); + + if (urb) { + urb->status = -ENOENT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(!usb_hcd_check_unlink_urb(&hcd->usb_hcd, urb,0)) + usb_hcd_unlink_urb_from_ep(&hcd-> + usb_hcd,urb); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(&hcd->usb_hcd, urb); +#else + usb_hcd_giveback_urb(&hcd->usb_hcd, urb, + urb->status); +#endif + } + } + } + + spin_lock(&hcd->lock); + } + + /* When there is no more PTDs queued for scheduling or removal + * clear the buffer status to indicate there are no more PTDs for + * processing and set the skip map to 1 to indicate that the first + * PTD is also the last PTD. + */ + + if (hcd->periodic_more_urb) { + int status = 0; + iso_dbg(ISO_DBG_INFO, + "[phcd_iso_handler]: No more PTDs queued\n"); + hcd->periodic_sched = 0; + phcd_store_urb_pending(hcd, hcd->periodic_more_urb, NULL, + &status); + hcd->periodic_more_urb = 0; + } +exit: + iso_dbg(ISO_DBG_ENTRY, "-- %s: Exit\n", __FUNCTION__); +} /* end of pehci_hcd_iso_worker */ + +#endif /* CONFIG_ISO_SUPPORT */ + +/*interrupt transfer handler*/ +/******************************************************** + 1. read done map + 2. read the ptd to see any errors + 3. copy the payload to and from + 4. update ehci td + 5. make new ptd if transfer there and earlier done + 6. schedule + *********************************************************/ +static void +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +pehci_hcd_intl_worker(phci_hcd * hcd, struct pt_regs *regs) +#else +pehci_hcd_intl_worker(phci_hcd * hcd) +#endif +{ + int i = 0; + u16 donemap = 0, donetoclear; + u16 mask = 0x1, index = 0; + u16 pendingmap = 0; + u16 location = 0; + u32 length = 0; + u16 skipmap = 0; + u16 ormask = 0; + u32 usofstatus = 0; + struct urb *urb; + struct ehci_qtd *qtd = 0; + struct ehci_qh *qh = 0; + + struct _isp1763_qhint *qhint = &hcd->qhint; + + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + struct isp1763_mem_addr *mem_addr = 0; + u16 dontschedule = 0; + + ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL]); + pendingmap = ptd_map_buff->pending_ptd_bitmap; + + /*read the done map for interrupt transfers */ + donetoclear = donemap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttddonemap, donemap); + if (donemap) { + /*skip done tds */ + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + skipmap |= donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap); + donemap |= pendingmap; + } + /*if sof interrupt is enabled */ +#ifdef MSEC_INT_BASED + else { + /*if there is something pending , put this transfer in */ + if (ptd_map_buff->pending_ptd_bitmap) { + pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8) + TD_PTD_BUFF_TYPE_INTL, + 1); + } + //return 0; + goto exit; + } +#else + else { + goto exit; + //return 0; + } + +#endif + + + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.int_irq_mask_or, + ormask); + /*process all the endpoints first those are done */ + donetoclear = donemap; + while (donetoclear) { + /*index is the number of endpoints open currently */ + index = donetoclear & mask; + donetoclear &= ~mask; + mask <<= 1; + /*what if we are in the middle of schedule + where nothing is done */ + if (!index) { + location++; + continue; + } + + /*read our td_ptd_map */ + td_ptd_map = &ptd_map_buff->map_list[location]; + + /*if this one is already in the removal */ + if (td_ptd_map->state == TD_PTD_REMOVE || + td_ptd_map->state == TD_PTD_NEW) { + pehci_check("interrupt td is being removed\n"); + /*this will be handled by urb_remove */ + /*if this is last urb no need to complete it again */ + donemap &= ~td_ptd_map->ptd_bitmap; + /*if there is something pending */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + continue; + } + + + /*if we found something already in */ + if (!(skipmap & td_ptd_map->ptd_bitmap)) { + pehci_check("intr td_ptd_map %x,skipnap %x\n", + td_ptd_map->ptd_bitmap, skipmap); + donemap &= ~td_ptd_map->ptd_bitmap; + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap;; + location++; + continue; + } + + + if (td_ptd_map->state == TD_PTD_NEW) { + pehci_check + ("interrupt not come here, map %x,location %d\n", + td_ptd_map->ptd_bitmap, location); + donemap &= ~td_ptd_map->ptd_bitmap; + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + donemap &= ~td_ptd_map->ptd_bitmap; + location++; + continue; + } + + /*move to the next schedule */ + location++; + /*endpoint, td, urb and memory + * for current transfer*/ + qh = td_ptd_map->qh; + qtd = td_ptd_map->qtd; + if (qtd->state & QTD_STATE_NEW) { + /*we need to schedule it */ + goto schedule; + } + urb = qtd->urb; + mem_addr = &qtd->mem_addr; + + /*clear the irq mask for this transfer */ + ormask &= ~td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, + ormask); + + ptd_map_buff->active_ptds--; + memset(qhint, 0, sizeof(struct _isp1763_qhint)); + + /*read this ptd from the ram address,address is in the + td_ptd_map->ptd_header_addr */ + isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr, 0, + (u32 *) (qhint), PHCI_QHA_LENGTH, 0); + +#ifdef PTD_DUMP_COMPLETE + printk("INTL PTD header after COMPLETION\n"); + printk("CDW0: 0x%08X\n", qhint->td_info1); + printk("CDW1: 0x%08X\n", qhint->td_info2); + printk("CDW2: 0x%08X\n", qhint->td_info3); + printk("CDW3: 0x%08X\n", qhint->td_info4); +#endif + + /*statuc of 8 uframes */ + for (i = 0; i < 8; i++) { + /*take care of errors */ + usofstatus = qhint->td_info5 >> (8 + i * 3); + switch (usofstatus & 0x7) { + case INT_UNDERRUN: + pehci_print("under run , %x\n", usofstatus); + break; + case INT_EXACT: + pehci_print("transaction error, %x\n", + usofstatus); + break; + case INT_BABBLE: + pehci_print("babble error, %x\n", usofstatus); + break; + } + } + + if (urb->dev->speed != USB_SPEED_HIGH) { + /*length is 1K for full/low speed device */ + length = PTD_XFERRED_NONHSLENGTH(qhint->td_info4); + } else { + /*length is 32K for high speed device */ + length = PTD_XFERRED_LENGTH(qhint->td_info4); + } + + pehci_hcd_update_error_status(qhint->td_info4, urb); + /*halted, need to finish all the transfer on this endpoint */ + if (qhint->td_info4 & PTD_STATUS_HALTED) { + qtd->state |= QTD_STATE_LAST; + /*in case of halt, next transfer will start with toggle zero, + *USB speck, 5.8.5*/ + qh->datatoggle = td_ptd_map->datatoggle = 0; + donemap &= ~td_ptd_map->ptd_bitmap; + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + dontschedule = 1; + goto copylength; + } + + + copylength: + /*preserve the current data toggle */ + qh->datatoggle = td_ptd_map->datatoggle = + PTD_NEXTTOGGLE(qhint->td_info4); + /*copy data from the host */ + switch (PTD_PID(qhint->td_info2)) { + case IN_PID: + if (length && (length <= MAX_PTD_BUFFER_SIZE)) + /*do read only when there is somedata */ + isp1763_mem_read(hcd->dev, + (u32) mem_addr->phy_addr, 0, + urb->transfer_buffer + + urb->actual_length, length, 0); + + case OUT_PID: + urb->actual_length += length; + qh->hw_current = qtd->hw_next; + phci_hcd_mem_free(&qtd->mem_addr); + qtd->state &= ~QTD_STATE_NEW; + qtd->state |= QTD_STATE_DONE; + break; + } + + if (qtd->state & QTD_STATE_LAST) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs); +#else + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map); +#endif + if (dontschedule) { /*cleanup will start from drivers */ + dontschedule = 0; + continue; + } + + /*take the next if in the queue */ + if (!list_empty(&qh->qtd_list)) { + struct list_head *head; + /*last td of previous urb */ + head = &qh->qtd_list; + qtd = list_entry(head->next, struct ehci_qtd, + qtd_list); + td_ptd_map->qtd = qtd; + qh->hw_current = cpu_to_le32(qtd); + qh->qh_state = QH_STATE_LINKED; + + } else { + td_ptd_map->qtd = + (struct ehci_qtd *) le32_to_cpu(0); + qh->hw_current = cpu_to_le32(0); + qh->qh_state = QH_STATE_IDLE; + donemap &= ~td_ptd_map->ptd_bitmap; + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + td_ptd_map->state=TD_PTD_NEW; + continue; + } + + } + + schedule: + { + /*current td comes from qh->hw_current */ + ptd_map_buff->pending_ptd_bitmap &= + ~td_ptd_map->ptd_bitmap; + ormask |= td_ptd_map->ptd_bitmap; + ptd_map_buff->active_ptds++; + pehci_check + ("inter schedule next qtd %p, active tds %d\n", + qtd, ptd_map_buff->active_ptds); + pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map); + } + + } /*end of while */ + + + /*clear all the tds inside this routine */ + skipmap &= ~donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, skipmap); + ormask |= donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, ormask); +exit: + pehci_entry("-- %s: Exit\n", __FUNCTION__); + +// return (int)0; +} + +/*atl(bulk/control) transfer handler*/ +/*1. read done map + 2. read the ptd to see any errors + 3. copy the payload to and from + 4. update ehci td + 5. make new ptd if transfer there and earlier done + 6. schedule + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static void +pehci_hcd_atl_worker(phci_hcd * hcd, struct pt_regs *regs) +#else +static void +pehci_hcd_atl_worker(phci_hcd * hcd) +#endif +{ + u16 donemap = 0, donetoclear = 0; + u16 pendingmap = 0; + u32 rl = 0; + u16 mask = 0x1, index = 0; + u16 location = 0; + u32 nakcount = 0; + u32 active = 0; + u32 length = 0; + u16 skipmap = 0; + u16 tempskipmap = 0; + u16 ormask = 0; + struct urb *urb; + struct ehci_qtd *qtd = 0; + struct ehci_qh *qh; + struct _isp1763_qha atlqha; + struct _isp1763_qha *qha; + td_ptd_map_t *td_ptd_map; + td_ptd_map_buff_t *ptd_map_buff; + urb_priv_t *urbpriv = 0; + struct isp1763_mem_addr *mem_addr = 0; + u16 dontschedule = 0; + ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL]); + pendingmap = ptd_map_buff->pending_ptd_bitmap; + +#ifdef MSEC_INT_BASED + /*running on skipmap rather donemap, + some cases donemap may not be set + for complete transfer + */ + skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, skipmap); + tempskipmap = ~skipmap; + tempskipmap &= 0xffff; + + if (tempskipmap) { + donemap = + isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap, + donemap); + skipmap |= donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap); + qha = &atlqha; + donemap |= pendingmap; + tempskipmap &= ~donemap; + } else { + + /*if sof interrupt enabled */ + + /*if there is something pending , put this transfer in */ + if (pendingmap) { + pehci_hcd_schedule_pending_ptds(hcd, pendingmap, (u8) + TD_PTD_BUFF_TYPE_ATL, + 1); + } + goto exit; + } +#else + + donemap = isp1763_reg_read16(hcd->dev, hcd->regs.atltddonemap, donemap); + if (donemap) { + + + pehci_info("DoneMap Value in ATL Worker %x\n", donemap); + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + skipmap |= donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap); + qha = &atlqha; + } else { + pehci_info("Done Map Value is 0x%X \n", donemap); + pehci_entry("-- %s: Exit abnormally with DoneMap all zero \n", + __FUNCTION__); + goto exit; + + } +#endif + + /*read the interrupt mask registers */ + ormask = isp1763_reg_read16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + + + /*this map is used only to update and + * scheduling for the tds who are not + * complete. the tds those are complete + * new schedule will happen from + * td_ptd_submit_urb routine + * */ + donetoclear = donemap; + /*we will be processing skipped tds also */ + donetoclear |= tempskipmap; + /*process all the endpoints first those are done */ + while (donetoclear) { + /*index is the number of endpoint open currently */ + index = donetoclear & mask; + donetoclear &= ~mask; + mask <<= 1; + /*what if we are in the middle of schedule + where nothing is done + */ + if (!index) { + location++; + continue; + } + + /*read our td_ptd_map */ + td_ptd_map = &ptd_map_buff->map_list[location]; + + /*urb is in remove */ + if (td_ptd_map->state == TD_PTD_NEW || + td_ptd_map->state == TD_PTD_REMOVE) { + pehci_check + ("atl td is being removed,map %x, skipmap %x\n", + td_ptd_map->ptd_bitmap, skipmap); + pehci_check("temp skipmap %x, pendign map %x,done %x\n", + tempskipmap, pendingmap, donemap); + + /*unlink urb will take care of this */ + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + location++; + continue; + } + + + /*move to the next endpoint */ + location++; + /*endpoint, td, urb and memory + * for current endpoint*/ + qh = td_ptd_map->qh; + qtd = td_ptd_map->qtd; + if (!qh || !qtd) { + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + continue; + } +#ifdef MSEC_INT_BASED + /*new td must be scheduled */ + if ((qtd->state & QTD_STATE_NEW) /*&& + (pendingmap & td_ptd_map->ptd_bitmap) */ ) { + /*this td will come here first time from + *pending tds, so its qh->hw_current needs to + * adjusted + */ + qh->hw_current = QTD_NEXT(qtd->qtd_dma); + goto schedule; + } +#endif + urb = qtd->urb; + if (urb == NULL) { + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + continue; + } + urbpriv = (urb_priv_t *) urb->hcpriv; + mem_addr = &qtd->mem_addr; + +#ifdef MSEC_INT_BASED + /*check here for the td if its done */ + if (donemap & td_ptd_map->ptd_bitmap) { + /*nothing to do */ + ; + } else { + /*if td is not done, lets check how long + its been scheduled + */ + if (tempskipmap & td_ptd_map->ptd_bitmap) { + /*i will give 20 msec to complete */ + if (urbpriv->timeout < 20) { + urbpriv->timeout++; + continue; + } + urbpriv->timeout++; + /*otherwise check its status */ + } + + } +#endif + memset(qha, 0, sizeof(struct _isp1763_qha)); + + /*read this ptd from the ram address,address is in the + td_ptd_map->ptd_header_addr */ + isp1763_mem_read(hcd->dev, td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, 0); + +#ifdef PTD_DUMP_COMPLETE + printk("ATL PTD header after COMPLETION\n"); + printk("CDW0: 0x%08X\n", qha->td_info1); + printk("CDW1: 0x%08X\n", qha->td_info2); + printk("CDW2: 0x%08X\n", qha->td_info3); + printk("CDW3: 0x%08X\n", qha->td_info4); +#endif + +#ifdef MSEC_INT_BASED + /*since we are running on skipmap + tds will be checked for completion state + */ + if ((qha->td_info1 & QHA_VALID)) { + + pehci_check + ("pendign map %x, donemap %x, tempskipmap %x\n", + pendingmap, donemap, tempskipmap); + /*this could be one of the unprotected urbs, clear it */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*here also we need to increment the tds timeout count */ + urbpriv->timeout++; + continue; + } else { + /*this td is going to be done, + this td could be the one un-skipped but no donemap or + maybe it could be one of those where we get unprotected urbs, + so checking against tempskipmap may not give us correct td + */ + + skipmap |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + + /*of course this is going to be as good + as td that is done and donemap is set + also skipmap is set + */ + donemap |= td_ptd_map->ptd_bitmap; + } +#endif + /*clear the corrosponding mask register */ + ormask &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + + ptd_map_buff->active_ptds--; + + urbpriv->timeout = 0; + + /*take care of errors */ + pehci_hcd_update_error_status(qha->td_info4, urb); + /*halted, need to finish all the transfer on this endpoint */ + if (qha->td_info4 & PTD_STATUS_HALTED) { + + printk(KERN_NOTICE "Endpoint is halted\n"); + qtd->state |= QTD_STATE_LAST; + + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*in case pending */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*in case of halt, next transfer will start with toggle + zero,USB speck, 5.8.5 */ + qh->datatoggle = td_ptd_map->datatoggle = 0; + /*cleanup the ping */ + qh->ping = 0; + /*force cleanup after this */ + dontschedule = 1; + goto copylength; + } + + + + /*read the reload count */ + rl = (qha->td_info3 >> 23); + rl &= 0xf; + + + + /*if there is a transaction error and the status is not halted, + * process whatever the length we got.if the length is what we + * expected complete the transfer*/ + if ((qha->td_info4 & PTD_XACT_ERROR) && + !(qha->td_info4 & PTD_STATUS_HALTED) && + (qha->td_info4 & QHA_ACTIVE)) { + + if (PTD_XFERRED_LENGTH(qha->td_info4) == qtd->length) { + ; /*nothing to do its fake */ + } else { + + pehci_print + ("xact error, info1 0x%08x,info4 0x%08x\n", + qha->td_info1, qha->td_info4); + + /*if this is the case then we need to + resubmit the td again */ + qha->td_info1 |= QHA_VALID; + skipmap &= ~td_ptd_map->ptd_bitmap; + ormask |= td_ptd_map->ptd_bitmap; + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + + /*set the retry count to 3 again */ + qha->td_info4 |= (rl << 19); + /*set the active bit, if cleared, will be cleared if we have some length */ + qha->td_info4 |= QHA_ACTIVE; + + /*clear the xact error */ + qha->td_info4 &= ~PTD_XACT_ERROR; + isp1763_reg_write16(hcd->dev, + hcd->regs.atl_irq_mask_or, + ormask); + + /*copy back into the header, payload is already + * present no need to write again + */ + isp1763_mem_write(hcd->dev, + td_ptd_map->ptd_header_addr, + 0, (u32 *) (qha), + PHCI_QHA_LENGTH, 0); + /*unskip this td */ + isp1763_reg_write16(hcd->dev, + hcd->regs.atltdskipmap, + skipmap); + continue; + } + goto copylength; + } + + /*check for the nak count and active condition + * to reload the ptd if needed*/ + nakcount = qha->td_info4 >> 19; + nakcount &= 0xf; + active = qha->td_info4 & QHA_ACTIVE; + /*if nak count is zero and active bit is set , it + *means that device is naking and need to reload + *the same td*/ + if (!nakcount && active) { + pehci_info("%s: ptd is going for reload,length %d\n", + __FUNCTION__, length); + /*make this td valid */ + qha->td_info1 |= QHA_VALID; + donemap &= ((~td_ptd_map->ptd_bitmap & 0xffff)); + /*just like fresh td */ + + /*set the retry count to 3 again */ + qha->td_info4 |= (rl << 19); + qha->td_info4 &= ~0x3; + qha->td_info4 |= (0x2 << 23); + ptd_map_buff->active_ptds++; + skipmap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + ormask |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, + ormask); + /*copy back into the header, payload is already + * present no need to write again */ + isp1763_mem_write(hcd->dev, td_ptd_map->ptd_header_addr, + 0, (u32 *) (qha), PHCI_QHA_LENGTH, 0); + /*unskip this td */ + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + continue; + } + + copylength: + /*read the length transferred */ + length = PTD_XFERRED_LENGTH(qha->td_info4); + + + /*short complete in case of BULK only */ + if ((length < qtd->length) && usb_pipebulk(urb->pipe)) { + + /*if current ptd is not able to fetech enough data as + * been asked then device has no data, so complete this transfer + * */ + /*can we complete our transfer here */ + if ((urb->transfer_flags & URB_SHORT_NOT_OK)) { + pehci_check + ("short read, length %d(expected %d)\n", + length, qtd->length); + urb->status = -EREMOTEIO; + /*if this is the only td,donemap will be cleared + at completion, otherwise take the next one + */ + donemap &= ((~td_ptd_map->ptd_bitmap) & 0xffff); + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + /*force the cleanup from here */ + dontschedule = 1; + } + + /*this will be the last td,in case of short read/write */ + /*donemap, pending maps will be handled at the while scheduling or completion */ + qtd->state |= QTD_STATE_LAST; + + } + /*preserve the current data toggle */ + qh->datatoggle = td_ptd_map->datatoggle = + PTD_NEXTTOGGLE(qha->td_info4); + qh->ping = PTD_PING_STATE(qha->td_info4); + /*copy data from */ + switch (PTD_PID(qha->td_info2)) { + case IN_PID: + qh->ping = 0; + /*do read only when there is some data */ + if (length && (length <= HC_ATL_PL_SIZE)) { + isp1763_mem_read(hcd->dev, + (u32) mem_addr->phy_addr, 0, + (u32*) (le32_to_cpu(qtd->hw_buf[0])), length, 0); +#if 0 + // printk("IN PayLoad length:%d\n", length); + if(length<=4) { + int i=0; + int *data_addr= qtd->hw_buf[0]; + printk("\n"); + for(i=0;i<length;i+=4) printk("[0x%X] ",*data_addr++); + printk("\n"); + } +#endif + } + + case OUT_PID: + urb->actual_length += length; + qh->hw_current = qtd->hw_next; + phci_hcd_mem_free(&qtd->mem_addr); + qtd->state |= QTD_STATE_DONE; + + break; + case SETUP_PID: + qh->hw_current = qtd->hw_next; + phci_hcd_mem_free(&qtd->mem_addr); + qtd->state |= QTD_STATE_DONE; + break; + } + + if (qtd->state & QTD_STATE_LAST) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, regs); +#else + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map); +#endif + if (dontschedule) { /*cleanup will start from drivers */ + dontschedule = 0; + /*so that we can take next one */ + qh->qh_state = QH_STATE_TAKE_NEXT; + continue; + } + /*take the next if in the queue */ + if (!list_empty(&qh->qtd_list)) { + struct list_head *head; + /*last td of previous urb */ + head = &qh->qtd_list; + qtd = list_entry(head->next, struct ehci_qtd, + qtd_list); + td_ptd_map->qtd = qtd; + qh->hw_current = cpu_to_le32(qtd); + qh->qh_state = QH_STATE_LINKED; + + } else { + td_ptd_map->qtd = + (struct ehci_qtd *) le32_to_cpu(0); + qh->hw_current = cpu_to_le32(0); + qh->qh_state = QH_STATE_TAKE_NEXT; + donemap &= ((~td_ptd_map->ptd_bitmap & 0xffff)); + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + continue; + } + } + +#ifdef MSEC_INT_BASED + schedule: +#endif + { + /*current td comes from qh->hw_current */ + ptd_map_buff->pending_ptd_bitmap &= + ((~td_ptd_map->ptd_bitmap) & 0xffff); + td_ptd_map->qtd = + (struct ehci_qtd + *) (le32_to_cpu(qh->hw_current)); + qtd = td_ptd_map->qtd; + ormask |= td_ptd_map->ptd_bitmap; + ptd_map_buff->active_ptds++; + pehci_hcd_qtd_schedule(hcd, qtd, qh, td_ptd_map); + } + + } /*end of while */ + +/*clear all the tds inside this routine*/ + skipmap &= ((~donemap) & 0xffff); + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, skipmap); + ormask |= donemap; + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, ormask); +exit: + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} + +/*--------------------------------------------------------* + root hub functions + *--------------------------------------------------------*/ + +/*return root hub descriptor, can not fail*/ +static void +pehci_hub_descriptor(phci_hcd * hcd, struct usb_hub_descriptor *desc) +{ + u32 ports = 0; + u16 temp = 0; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + + ports = 0x11; + ports = ports & 0xf; + + pehci_info("%s: number of ports %d\n", __FUNCTION__, ports); + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; + + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + +// memset(&desc->DeviceRemovable[0], 0, temp); +// memset(&desc->PortPwrCtrlMask[temp], 0xff, temp); + memset(&desc->u.hs.DeviceRemovable[0], 0, temp); + memset(&desc->u.hs.PortPwrCtrlMask[temp], 0xff, temp); + + + temp = 0x0008; /* per-port overcurrent reporting */ + temp |= 0x0001; /* per-port power control */ + temp |= 0x0080; /* per-port indicators (LEDs) */ + desc->wHubCharacteristics = cpu_to_le16(temp); + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} + +/*after reset on root hub, + * device high speed or non-high speed + * */ +static int +phci_check_reset_complete(phci_hcd * hcd, int index, int port_status) +{ + pehci_print("check reset complete\n"); + if (!(port_status & PORT_CONNECT)) { + hcd->reset_done[index] = 0; + return port_status; + } + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + printk("port %d full speed --> companion\n", index + 1); + port_status |= PORT_OWNER; + isp1763_reg_write32(hcd->dev, hcd->regs.ports[index], + port_status); + + } else { + pehci_print("port %d high speed\n", index + 1); + } + + return port_status; + +} + +/*----------------------------------------------* + host controller initialization, removal functions + *----------------------------------------------*/ + + +/*initialize all three buffer(iso/atl/int) type headers*/ +static void +pehci_hcd_init_map_buffers(phci_hcd * phci) +{ + td_ptd_map_buff_t *ptd_map_buff; + u8 buff_type, ptd_index; + u32 bitmap; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + pehci_print("phci_init_map_buffers(phci = 0x%p)\n", phci); + /* initialize for each buffer type */ + for (buff_type = 0; buff_type < TD_PTD_TOTAL_BUFF_TYPES; buff_type++) { + ptd_map_buff = &(td_ptd_map_buff[buff_type]); + ptd_map_buff->buffer_type = buff_type; + ptd_map_buff->active_ptds = 0; + ptd_map_buff->total_ptds = 0; + /*each bufer type can have atleast 32 ptds */ + ptd_map_buff->max_ptds = 16; + ptd_map_buff->active_ptd_bitmap = 0; + /*everything skipped */ + /*nothing is pending */ + ptd_map_buff->pending_ptd_bitmap = 0x00000000; + + /* For each ptd index of this buffer, set the fiedls */ + bitmap = 0x00000001; + for (ptd_index = 0; ptd_index < TD_PTD_MAX_BUFF_TDS; + ptd_index++) { + /*datatoggle zero */ + ptd_map_buff->map_list[ptd_index].datatoggle = 0; + /*td state is not used */ + ptd_map_buff->map_list[ptd_index].state = TD_PTD_NEW; + /*no endpoint, no qtd */ + ptd_map_buff->map_list[ptd_index].qh = NULL; + ptd_map_buff->map_list[ptd_index].qtd = NULL; + ptd_map_buff->map_list[ptd_index].ptd_header_addr = + 0xFFFF; + } /* for( ptd_index */ + } /* for(buff_type */ + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} /* phci_init_map_buffers */ + + +/*put the host controller into operational mode + * called phci_hcd_start routine, + * return 0, success else + * timeout, fails*/ + +static int +pehci_hcd_start_controller(phci_hcd * hcd) +{ + u32 temp = 0; + u32 command = 0; + int retval = 0; + pehci_entry("++ %s: Entered\n", __FUNCTION__); + printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__); + + + command = isp1763_reg_read16(hcd->dev, hcd->regs.command, command); + printk(KERN_NOTICE "HC Command Reg val ...1 %x\n", command); + + /*initialize the host controller */ + command |= CMD_RUN; + + isp1763_reg_write16(hcd->dev, hcd->regs.command, command); + + + command &= 0; + + command = isp1763_reg_read16(hcd->dev, hcd->regs.command, command); + printk(KERN_NOTICE "HC Command Reg val ...2 %x\n", command); + + /*should be in operation in 1000 usecs */ + if ((retval = + pehci_hcd_handshake(hcd, hcd->regs.command, CMD_RUN, CMD_RUN, + 100000))) { + err("Host is not up(CMD_RUN) in 1000 usecs\n"); + return retval; + } + + printk(KERN_NOTICE "ISP1763 HC is running \n"); + + + /*put the host controller to ehci mode */ + command &= 0; + command |= 1; + + isp1763_reg_write16(hcd->dev, hcd->regs.configflag, command); + mdelay(5); + + temp = isp1763_reg_read16(hcd->dev, hcd->regs.configflag, temp); + pehci_print("%s: Config Flag reg value: 0x%08x\n", __FUNCTION__, temp); + + /*check if ehci mode switching is correct or not */ + if ((retval = + pehci_hcd_handshake(hcd, hcd->regs.configflag, 1, 1, 100))) { + err("Host is not into ehci mode in 100 usecs\n"); + return retval; + } + + mdelay(5); + + pehci_entry("-- %s: Exit\n", __FUNCTION__); + printk(KERN_NOTICE "-- %s: Exit\n", __FUNCTION__); + return retval; +} + + +/*enable the interrupts + *called phci_1763_start routine + * return void*/ +static void +pehci_hcd_enable_interrupts(phci_hcd * hcd) +{ + u32 temp = 0; + pehci_entry("++ %s: Entered\n", __FUNCTION__); + printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__); + /*disable the interrupt source */ + temp &= 0; + /*clear all the interrupts that may be there */ + temp |= INTR_ENABLE_MASK; + isp1763_reg_write16(hcd->dev, hcd->regs.interrupt, temp); + + /*enable interrupts */ + temp = 0; + +#ifdef OTG_PACKAGE + temp |= INTR_ENABLE_MASK | HC_OTG_INT; +#else + temp |= INTR_ENABLE_MASK; +#endif + pehci_print("%s: enabled mask 0x%08x\n", __FUNCTION__, temp); + isp1763_reg_write16(hcd->dev, hcd->regs.interruptenable, temp); + + temp = isp1763_reg_read16(hcd->dev, hcd->regs.interruptenable, temp); + pehci_print("%s: Intr enable reg value: 0x%08x\n", __FUNCTION__, temp); + +#ifdef HCD_PACKAGE + temp = 0; + temp = isp1763_reg_read32(hcd->dev, HC_INT_THRESHOLD_REG, temp); +// temp |= 0x0800000F; + temp |= 0x0100000F;//125 micro second minimum width between two edge interrupts, 500ns int will remain low + // 15/30MHz=500 ns + isp1763_reg_write32(hcd->dev, HC_INT_THRESHOLD_REG, temp); +#endif + /*enable the global interrupt */ + temp &= 0; + temp = isp1763_reg_read16(hcd->dev, hcd->regs.hwmodecontrol, temp); + temp |= 0x01; /*enable the global interrupt */ +#ifdef EDGE_INTERRUPT + temp |= 0x02; /*enable the edge interrupt */ +#endif + +#ifdef POL_HIGH_INTERRUPT + temp |= 0x04; /* enable interrupt polarity high */ +#endif + + isp1763_reg_write16(hcd->dev, hcd->regs.hwmodecontrol, temp); + + /*maximum rate is one msec */ + /*enable the atl interrupts OR and AND mask */ + temp = 0; + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_and, temp); + temp = 0; + isp1763_reg_write16(hcd->dev, hcd->regs.atl_irq_mask_or, temp); + temp = 0; + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_and, temp); + temp = 0x0; + isp1763_reg_write16(hcd->dev, hcd->regs.int_irq_mask_or, temp); + temp = 0; + isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_and, temp); + temp = 0xffff; + isp1763_reg_write16(hcd->dev, hcd->regs.iso_irq_mask_or, temp); + + temp = isp1763_reg_read16(hcd->dev, hcd->regs.iso_irq_mask_or, temp); + pehci_print("%s:Iso irq mask reg value: 0x%08x\n", __FUNCTION__, temp); + + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} + +/*initialize the host controller register map from Isp1763 to EHCI */ +static void +pehci_hcd_init_reg(phci_hcd * hcd) +{ + pehci_entry("++ %s: Entered\n", __FUNCTION__); + /* scratch pad for the test */ + hcd->regs.scratch = HC_SCRATCH_REG; + + /*make a copy of our interrupt locations */ + hcd->regs.command = HC_USBCMD_REG; + hcd->regs.usbstatus = HC_USBSTS_REG; + hcd->regs.usbinterrupt = HC_INTERRUPT_REG_EHCI; + + hcd->regs.hcsparams = HC_SPARAMS_REG; + hcd->regs.frameindex = HC_FRINDEX_REG; + + /*transfer specific registers */ + hcd->regs.hwmodecontrol = HC_HWMODECTRL_REG; + hcd->regs.interrupt = HC_INTERRUPT_REG; + hcd->regs.interruptenable = HC_INTENABLE_REG; + hcd->regs.atl_irq_mask_and = HC_ATL_IRQ_MASK_AND_REG; + hcd->regs.atl_irq_mask_or = HC_ATL_IRQ_MASK_OR_REG; + + hcd->regs.int_irq_mask_and = HC_INT_IRQ_MASK_AND_REG; + hcd->regs.int_irq_mask_or = HC_INT_IRQ_MASK_OR_REG; + hcd->regs.iso_irq_mask_and = HC_ISO_IRQ_MASK_AND_REG; + hcd->regs.iso_irq_mask_or = HC_ISO_IRQ_MASK_OR_REG; + hcd->regs.buffer_status = HC_BUFFER_STATUS_REG; + hcd->regs.interruptthreshold = HC_INT_THRESHOLD_REG; + /*initialization specific */ + hcd->regs.reset = HC_RESET_REG; + hcd->regs.configflag = HC_CONFIGFLAG_REG; + hcd->regs.ports[0] = HC_PORTSC1_REG; + hcd->regs.ports[1] = 0; /*port1,port2,port3 status reg are removed */ + hcd->regs.ports[2] = 0; + hcd->regs.ports[3] = 0; + hcd->regs.pwrdwn_ctrl = HC_POWER_DOWN_CONTROL_REG; + /*transfer registers */ + hcd->regs.isotddonemap = HC_ISO_PTD_DONEMAP_REG; + hcd->regs.isotdskipmap = HC_ISO_PTD_SKIPMAP_REG; + hcd->regs.isotdlastmap = HC_ISO_PTD_LASTPTD_REG; + + hcd->regs.inttddonemap = HC_INT_PTD_DONEMAP_REG; + + hcd->regs.inttdskipmap = HC_INT_PTD_SKIPMAP_REG; + hcd->regs.inttdlastmap = HC_INT_PTD_LASTPTD_REG; + + hcd->regs.atltddonemap = HC_ATL_PTD_DONEMAP_REG; + hcd->regs.atltdskipmap = HC_ATL_PTD_SKIPMAP_REG; + hcd->regs.atltdlastmap = HC_ATL_PTD_LASTPTD_REG; + + pehci_entry("-- %s: Exit\n", __FUNCTION__); +} + + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static void +pehci_interrupt_handler(phci_hcd * hcd, struct pt_regs *regs) +{ + spin_lock(&hcd->lock); +#ifdef CONFIG_ISO_SUPPORT + phcd_iso_handler(hcd, regs); +#endif + pehci_hcd_intl_worker(hcd, regs); + pehci_hcd_atl_worker(hcd, regs); + spin_unlock(&hcd->lock); + return; +} +#else +static void +pehci_interrupt_handler(phci_hcd * hcd) +{ + spin_lock(&hcd->lock); +#ifdef CONFIG_ISO_SUPPORT + pehci_hcd_iso_worker(hcd); +#endif + pehci_hcd_intl_worker(hcd); + pehci_hcd_atl_worker(hcd); + spin_unlock(&hcd->lock); + return; +} +#endif +irqreturn_t +pehci_hcd_irq(struct isp1763_dev * dev, void *__irq_data, struct pt_regs * regs) +{ + + int work = 0; + phci_hcd *pehci_hcd; + u32 intr = 0; + u32 resume=0; + u32 temp=0; + u32 irq_mask = 0; + + struct usb_hcd *usb_hcd = (struct usb_hcd *) __irq_data; + + if (!(usb_hcd->state & USB_STATE_READY)) { + info("interrupt handler state not ready yet\n"); + usb_hcd->state=USB_STATE_READY; + // return IRQ_NONE; + } + + /*our host */ + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + dev = pehci_hcd->dev; + + intr = dev->int_reg; + + + if (atomic_read(&pehci_hcd->nuofsofs)) { + return IRQ_HANDLED; + } + atomic_inc(&pehci_hcd->nuofsofs); + + irq_mask=isp1763_reg_read32(dev,HC_USBSTS_REG,0); + isp1763_reg_write32(dev,HC_USBSTS_REG,irq_mask); + if(irq_mask & 0x4){ // port status register. + if(intr & 0x50) { // OPR register change + temp=isp1763_reg_read32(dev,HC_PORTSC1_REG,0); + if(temp & 0x4){ // Force resume bit is set + if (dev) { + if (dev->driver) { + if (dev->driver->resume) { + + spin_lock(&pehci_hcd->lock);/*This spin lock will be depends on syspem can be moved start of the function also*/ + dev->driver->resume(dev); + spin_unlock(&pehci_hcd->lock); + resume=1; + } + } + } + } + } + } + + set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags); + +#ifndef THREAD_BASED +/*-----------------------------------------------------------*/ +#ifdef MSEC_INT_BASED + work = 1; +#else + if (intr & (HC_MSEC_INT & INTR_ENABLE_MASK)) { + work = 1; /* phci_iso_worker(hcd); */ + } + +#ifdef USBNET + if (intr & HC_MSOF_INT ) { + struct list_head *pos, *q; + + list_for_each_safe(pos, q, &pehci_hcd->cleanup_urb.urb_list) { + struct isp1763_async_cleanup_urb *tmp; + + tmp = list_entry(pos, struct isp1763_async_cleanup_urb, urb_list); + if (tmp) { + usb_hcd_giveback_urb(usb_hcd, tmp->urb, tmp->urb->status); + list_del(pos); + if(tmp) + kfree(tmp); + } + } + isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK ); + } +#endif + + + if (intr & (HC_INTL_INT & INTR_ENABLE_MASK)) { + spin_lock(&pehci_hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_intl_worker(pehci_hcd, regs); +#else + pehci_hcd_intl_worker(pehci_hcd); +#endif + spin_unlock(&pehci_hcd->lock); + work = 0; /*phci_intl_worker(hcd); */ + } + + if (intr & (HC_ATL_INT & INTR_ENABLE_MASK)) { + spin_lock(&pehci_hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_atl_worker(pehci_hcd, regs); +#else + pehci_hcd_atl_worker(pehci_hcd); +#endif + spin_unlock(&pehci_hcd->lock); + work = 0; /*phci_atl_worker(hcd); */ + } +#ifdef CONFIG_ISO_SUPPORT + if (intr & (HC_ISO_INT & INTR_ENABLE_MASK)) { + spin_lock(&pehci_hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_iso_worker(pehci_hcd); +#else + pehci_hcd_iso_worker(pehci_hcd); +#endif + spin_unlock(&pehci_hcd->lock); + work = 0; /*phci_atl_worker(hcd); */ + } +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + if (work){ + pehci_interrupt_handler(pehci_hcd, regs); + } +#else + if (work){ + pehci_interrupt_handler(pehci_hcd); + } +#endif + +/*-----------------------------------------------------------*/ +#else + if ((intr & (HC_INTL_INT & INTR_ENABLE_MASK)) ||(intr & (HC_ATL_INT & INTR_ENABLE_MASK))) + { //send + st_UsbIt_Msg_Struc *stUsbItMsgSnd ; + + stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC); + if (!stUsbItMsgSnd) return -ENOMEM; + + memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd)); + + stUsbItMsgSnd->usb_hcd = usb_hcd; + stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_ISR; + list_add_tail(&(stUsbItMsgSnd->list), &(g_messList.list)); + + pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus); + if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0)) + { + pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus); + g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1; + wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead)); + } + } +/*-----------------------------------------------------------*/ +#endif + + atomic_dec(&pehci_hcd->nuofsofs); + if(resume){ + usb_hcd_poll_rh_status(usb_hcd); + } + return IRQ_HANDLED; +} + +/*reset the host controller + *called phci_hcd_start routine + *return 0, success else + *timeout, fails*/ +static int +pehci_hcd_reset(struct usb_hcd *usb_hcd) +{ + u32 command = 0; + u32 temp = 0; + phci_hcd *hcd = usb_hcd_to_pehci_hcd(usb_hcd); + printk(KERN_NOTICE "++ %s: Entered\n", __FUNCTION__); + pehci_hcd_init_reg(hcd); + printk("chipid %x \n", isp1763_reg_read32(hcd->dev, HC_CHIP_ID_REG, temp)); //0x70 + + /*reset the atx controller */ + temp &= 0; + temp |= 8; + isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp); + mdelay(10); + + /*reset the host controller */ + temp &= 0; + temp |= 1; + isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp); + + command = 0; + do { + + temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp); + mdelay(10); + command++; + if (command > 100) { + printk("not able to reset\n"); + break; + } + } while (temp & 0x01); + + + /*reset the ehci controller registers */ + temp = 0; + temp |= (1 << 1); + isp1763_reg_write16(hcd->dev, hcd->regs.reset, temp); + command = 0; + do { + temp = isp1763_reg_read16(hcd->dev, hcd->regs.reset, temp); + mdelay(10); + command++; + if (command > 100) { + printk("not able to reset\n"); + break; + } + } while (temp & 0x02); + + /*read the command register */ + command = isp1763_reg_read16(hcd->dev, hcd->regs.command, command); + + command |= CMD_RESET; + /*write back and wait for, 250 msec */ + isp1763_reg_write16(hcd->dev, hcd->regs.command, command); + /*wait for maximum 250 msecs */ + mdelay(200); + printk("command %x\n", + isp1763_reg_read16(hcd->dev, hcd->regs.command, command)); + printk(KERN_NOTICE "-- %s: Exit \n", __FUNCTION__); + return 0; +} + +/*host controller initialize routine, + *called by phci_hcd_probe + * */ +static int +pehci_hcd_start(struct usb_hcd *usb_hcd) +{ + + int retval; + int count = 0; + phci_hcd *pehci_hcd = NULL; + u32 temp = 0; + u32 hwmodectrl = 0; + u32 ul_scratchval = 0; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + + spin_lock_init(&pehci_hcd->lock); + atomic_set(&pehci_hcd->nuofsofs, 0); + atomic_set(&pehci_hcd->missedsofs, 0); + + /*Initialize host controller registers */ + pehci_hcd_init_reg(pehci_hcd); + + /*reset the host controller */ + retval = pehci_hcd_reset(usb_hcd); + if (retval) { + err("phci_1763_start: error failing with status %x\n", retval); + return retval; + } + + hwmodectrl = + isp1763_reg_read16(pehci_hcd->dev, + pehci_hcd->regs.hwmodecontrol, hwmodectrl); +#ifdef DATABUS_WIDTH_16 + printk(KERN_NOTICE "Mode Ctrl Value before 16width: %x\n", hwmodectrl); + hwmodectrl &= 0xFFEF; /*enable the 16 bit bus */ + hwmodectrl |= 0x0400; /*enable common int */ +#else + printk(KERN_NOTICE "Mode Ctrl Value before 8width : %x\n", hwmodectrl); + hwmodectrl |= 0x0010; /*enable the 8 bit bus */ + hwmodectrl |= 0x0400; /*enable common int */ +#endif + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol, + hwmodectrl); + + hwmodectrl = + isp1763_reg_read16(pehci_hcd->dev, + pehci_hcd->regs.hwmodecontrol, hwmodectrl); + hwmodectrl |=0x9; //lock interface and enable global interrupt. + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.hwmodecontrol, + hwmodectrl); + printk(KERN_NOTICE "Mode Ctrl Value after buswidth: %x\n", hwmodectrl); + + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.scratch, 0x3344); + + ul_scratchval = + isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.scratch, + ul_scratchval); + printk(KERN_NOTICE "Scratch Reg Value : %x\n", ul_scratchval); + if (ul_scratchval != 0x3344) { + printk(KERN_NOTICE "Scratch Reg Value Mismatch: %x\n", + ul_scratchval); + + } + + + /*initialize the host controller initial values */ + /*disable all the buffer */ + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.buffer_status, 0); + /*skip all the transfers */ + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdskipmap, + NO_TRANSFER_ACTIVE); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdskipmap, + NO_TRANSFER_ACTIVE); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdskipmap, + NO_TRANSFER_ACTIVE); + /*clear done map */ + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltddonemap, + NO_TRANSFER_DONE); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttddonemap, + NO_TRANSFER_DONE); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotddonemap, + NO_TRANSFER_DONE); + +#ifdef HCD_PACKAGE + /*port1 as Host */ + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0400); + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x0080); + /*port2 as Host */ + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000); + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000); + + #if 0 /* do not use bit 1&2 for pure host application */ + ul_scratchval = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,0); + ul_scratchval |= 0x006; + isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG,ul_scratchval); + #endif + +#elif defined(HCD_DCD_PACKAGE) + + /*port1 as device */ + isp1763_reg_write16(pehci_hcd->dev,OTG_CTRL_SET_REG, + OTG_CTRL_DMPULLDOWN |OTG_CTRL_DPPULLDOWN | + OTG_CTRL_SW_SEL_HC_DC |OTG_CTRL_OTG_DISABLE); /* pure Device Mode and OTG disabled */ + + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0480); + /*port2 as host */ + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_SET_REG, 0x0000); + isp1763_reg_write16(pehci_hcd->dev, OTG_CTRL_CLEAR_REG, 0x8000); + ul_scratchval = + isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, + 0); +#endif + + /*enable interrupts */ + pehci_hcd_enable_interrupts(pehci_hcd); + + /*put controller into operational mode */ + retval = pehci_hcd_start_controller(pehci_hcd); + if (retval) { + err("phci_1763_start: error failing with status %x\n", retval); + return retval; + } + + /*Init the phci qtd <-> ptd map buffers */ + pehci_hcd_init_map_buffers(pehci_hcd); + + /*set last maps, for iso its only 1, else 32 tds bitmap */ + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.atltdlastmap, + 0x8000); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.inttdlastmap, 0x80); + isp1763_reg_write16(pehci_hcd->dev, pehci_hcd->regs.isotdlastmap, 0x01); + /*iso transfers are not active */ + pehci_hcd->next_uframe = -1; + pehci_hcd->periodic_sched = 0; + hwmodectrl = + isp1763_reg_read16(pehci_hcd->dev, + pehci_hcd->regs.hwmodecontrol, hwmodectrl); + + /*initialize the periodic list */ + for (count = 0; count < PTD_PERIODIC_SIZE; count++) { + pehci_hcd->periodic_list[count].framenumber = 0; + INIT_LIST_HEAD(&pehci_hcd->periodic_list[count].sitd_itd_head); + } + + + /*set the state of the host to ready, + * start processing interrupts + * */ + + usb_hcd->state = HC_STATE_RUNNING; + pehci_hcd->state = HC_STATE_RUNNING; + + + /*initialize root hub timer */ + init_timer(&pehci_hcd->rh_timer); + /*initialize watchdog */ + init_timer(&pehci_hcd->watchdog); + + temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, + temp); + + temp = 0x3e81bA0; +#if 0 + temp |= 0x306; +#endif + isp1763_reg_write32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, temp); + temp = isp1763_reg_read32(pehci_hcd->dev, HC_POWER_DOWN_CONTROL_REG, + temp); + printk(" Powerdown Reg Val: %x\n", temp); + + pehci_entry("-- %s: Exit\n", __FUNCTION__); + + return 0; +} + +static void +pehci_hcd_stop(struct usb_hcd *usb_hcd) +{ + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + + /* no more interrupts ... */ + if (usb_hcd->state == USB_STATE_RUNNING) { + mdelay(2); + } + if (in_interrupt()) { /* must not happen!! */ + pehci_info("stopped in_interrupt!\n"); + + return; + } + + /*power off our root hub */ + pehci_rh_control(usb_hcd, ClearPortFeature, USB_PORT_FEAT_POWER, + 1, NULL, 0); + + /*let the roothub power go off */ + mdelay(20); + pehci_entry("-- %s: Exit\n", __FUNCTION__); + + return; +} + + +/*submit urb , other than root hub*/ +static int +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct usb_host_endpoint *ep, + struct urb *urb, gfp_t mem_flags) +#else +pehci_hcd_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, gfp_t mem_flags) +#endif +{ + + struct list_head qtd_list; + struct ehci_qh *qh = 0; + phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + int status = 0; + int temp = 0, max = 0, num_tds = 0, mult = 0; + urb_priv_t *urb_priv = NULL; + unsigned long flags; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + + + if (unlikely(atomic_read(&urb->reject))) + return -EINVAL; + + INIT_LIST_HEAD(&qtd_list); + urb->transfer_flags &= ~EHCI_STATE_UNLINK; + + + temp = usb_pipetype(urb->pipe); + max = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe)); + + + if (hcdpowerdown == 1) { + printk("Enqueue hcd power down\n"); + return -EINVAL; + } + + + /*pathch to get the otg device */ + if (!hubdev || + (urb->dev->parent==usb_hcd->self.root_hub && + hubdev!=urb->dev)) { + if(urb->dev->parent== usb_hcd->self.root_hub) { + hubdev = urb->dev; + } + } + + switch (temp) { + case PIPE_INTERRUPT: + /*only one td */ + num_tds = 1; + mult = 1 + ((max >> 11) & 0x03); + max &= 0x07ff; + max *= mult; + + if (urb->transfer_buffer_length > max) { + err("interrupt urb length is greater then %d\n", max); + return -EINVAL; + } + + if (hubdev && urb->dev->parent == usb_hcd->self.root_hub) { + huburb = urb; + } + + break; + + case PIPE_CONTROL: + /*calculate the number of tds, follow 1 pattern */ + if (No_Data_Phase && No_Status_Phase) { + printk("Only SetUP Phase\n"); + num_tds = (urb->transfer_buffer_length == 0) ? 1 : + ((urb->transfer_buffer_length - + 1) / HC_ATL_PL_SIZE + 1); + } else if (!No_Data_Phase && No_Status_Phase) { + printk("SetUP Phase and Data Phase\n"); + num_tds = (urb->transfer_buffer_length == 0) ? 2 : + ((urb->transfer_buffer_length - + 1) / HC_ATL_PL_SIZE + 3); + } else if (!No_Data_Phase && !No_Status_Phase) { + num_tds = (urb->transfer_buffer_length == 0) ? 2 : + ((urb->transfer_buffer_length - + 1) / HC_ATL_PL_SIZE + 3); + } + + break; + + case PIPE_BULK: + num_tds = + (urb->transfer_buffer_length - 1) / HC_ATL_PL_SIZE + 1; + if ((urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % max)) { + num_tds++; + } + + break; + +#ifdef CONFIG_ISO_SUPPORT + case PIPE_ISOCHRONOUS: + /* Don't need to do anything here */ + break; +#endif + default: + return -EINVAL; /*not supported isoc transfers */ + + + } + +#ifdef CONFIG_ISO_SUPPORT + if (temp != PIPE_ISOCHRONOUS) { +#endif + /*make number of tds required */ + urb_priv = kmalloc(sizeof(urb_priv_t) + + num_tds * sizeof(struct ehci_qtd), + mem_flags); + if (!urb_priv) { + err("memory allocation error\n"); + return -ENOMEM; + } + + memset(urb_priv, 0, sizeof(urb_priv_t) + + num_tds * sizeof(struct ehci_qtd)); + INIT_LIST_HEAD(&urb_priv->qtd_list); + urb_priv->qtd[0] = NULL; + urb_priv->length = num_tds; + { + int i = 0; + /*allocate number of tds here. better to do this in qtd_make routine */ + for (i = 0; i < num_tds; i++) { + urb_priv->qtd[i] = + phci_hcd_qtd_allocate(mem_flags); + if (!urb_priv->qtd[i]) { + phci_hcd_urb_free_priv(pehci_hcd, + urb_priv, NULL); + return -ENOMEM; + } + } + } + /*keep a copy of this */ + urb->hcpriv = urb_priv; +#ifdef CONFIG_ISO_SUPPORT + } +#endif + + switch (temp) { + case PIPE_INTERRUPT: + phci_hcd_make_qtd(pehci_hcd, &urb_priv->qtd_list, urb, &status); + if (status < 0) { + return status; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qh = phci_hcd_submit_interrupt(pehci_hcd, ep, &urb_priv->qtd_list, urb, + &status); +#else + qh = phci_hcd_submit_interrupt(pehci_hcd, &urb_priv->qtd_list, urb, + &status); +#endif + if (status < 0) + return status; + break; + + case PIPE_CONTROL: + case PIPE_BULK: + +#ifdef THREAD_BASED + spin_lock_irqsave (&pehci_hcd->lock, flags); +#endif + phci_hcd_make_qtd(pehci_hcd, &qtd_list, urb, &status); + if (status < 0) { + return status; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + qh = phci_hcd_submit_async(pehci_hcd, ep, &qtd_list, urb, + &status); +#else + qh = phci_hcd_submit_async(pehci_hcd, &qtd_list, urb, &status); +#endif + +#ifdef THREAD_BASED + spin_unlock_irqrestore (&pehci_hcd->lock, flags); +#endif + + if (status < 0) { + return status; + } + break; +#ifdef CONFIG_ISO_SUPPORT + case PIPE_ISOCHRONOUS: + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_urb_enqueue]: URB Transfer buffer: 0x%08x\n", + (long) urb->transfer_buffer); + iso_dbg(ISO_DBG_DATA, + "[pehci_hcd_urb_enqueue]: URB Buffer Length: %d\n", + (long) urb->transfer_buffer_length); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + phcd_submit_iso(pehci_hcd, ep, urb, (unsigned long *) &status); +#else + spin_lock_irqsave(&pehci_hcd->lock, flags); + phcd_store_urb_pending(pehci_hcd, 0, urb, (int *) &status); + spin_unlock_irqrestore(&pehci_hcd->lock, flags); +#endif + + return status; + + break; +#endif + default: + return -ENODEV; + } /*end of switch */ + +#if (defined MSEC_INT_BASED) + return 0; +#elif (defined THREAD_BASED) +{ //send + st_UsbIt_Msg_Struc *stUsbItMsgSnd ; + unsigned long flags; + spin_lock_irqsave(&pehci_hcd->lock,flags); + + //local_irq_save(flags); /*disable interrupt*/ + stUsbItMsgSnd = (st_UsbIt_Msg_Struc *)kmalloc(sizeof(st_UsbIt_Msg_Struc), GFP_ATOMIC); + if (!stUsbItMsgSnd) + { + return -ENOMEM; + } + + memset(stUsbItMsgSnd, 0, sizeof(stUsbItMsgSnd)); + + stUsbItMsgSnd->usb_hcd = usb_hcd; + stUsbItMsgSnd->uIntStatus = NO_SOF_REQ_IN_REQ; + spin_lock(&enqueue_lock); + if(list_empty(&g_enqueueMessList.list)) + list_add_tail(&(stUsbItMsgSnd->list), &(g_enqueueMessList.list)); + spin_unlock(&enqueue_lock); + + pehci_print("\n------------- send mess : %d------------\n",stUsbItMsgSnd->uIntStatus); + + //local_irq_restore(flags); /*disable interrupt*/ + + spin_lock(&g_stUsbItThreadHandler.lock); + if ((g_stUsbItThreadHandler.phThreadTask != NULL) && (g_stUsbItThreadHandler.lThrdWakeUpNeeded == 0)) + { + pehci_print("\n------- wake up thread : %d-----\n",stUsbItMsgSnd->uIntStatus); + g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1; + wake_up(&(g_stUsbItThreadHandler.ulThrdWaitQhead)); + } + spin_unlock(&g_stUsbItThreadHandler.lock); + + spin_unlock_irqrestore(&pehci_hcd->lock,flags); + } + pehci_entry("-- %s: Exit\n",__FUNCTION__); + return 0; +#else + /*submit tds but iso */ + if (temp != PIPE_ISOCHRONOUS) + pehci_hcd_td_ptd_submit_urb(pehci_hcd, qh, qh->type); +#endif + pehci_entry("-- %s: Exit\n", __FUNCTION__); + return 0; + +} + +/*---------------------------------------------* + io request handlers + *---------------------------------------------*/ + +/*unlink urb*/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static int +pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb) +#else +static int +pehci_hcd_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + int status = 0; +#endif + int retval = 0; + td_ptd_map_buff_t *td_ptd_buf; + td_ptd_map_t *td_ptd_map; + struct ehci_qh *qh = 0; + u32 skipmap = 0; + u32 buffstatus = 0; + unsigned long flags; + struct ehci_qtd *qtd = 0; + struct usb_host_endpoint *ep; + + struct ehci_qtd *cancel_qtd = 0; /*added for stopping ptd*/ + struct urb *cancel_urb = 0; /*added for stopping ptd*/ + urb_priv_t *cancel_urb_priv = 0; /* added for stopping ptd */ + struct _isp1763_qha atlqha; + struct _isp1763_qha *qha; + struct isp1763_mem_addr *mem_addr = 0; + u32 ormask = 0; + struct list_head *qtd_list = 0; + urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv; + phci_hcd *hcd = usb_hcd_to_pehci_hcd(usb_hcd); + pehci_entry("++ %s: Entered\n", __FUNCTION__); + + pehci_info("device %d\n", urb->dev->devnum); + + if(urb_priv==NULL){ + printk("*******urb_priv is NULL******* %s: Entered\n", __FUNCTION__); + return 0; + } + spin_lock_irqsave(&hcd->lock, flags); + + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + // status = 0; + qh = urb_priv->qh; + if(qh==NULL) + break; + + td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_ATL]; + td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index]; + + /*if its already been removed */ + if (td_ptd_map->state == TD_PTD_NEW) { + break; + } +/* patch added for stopping Full speed PTD */ +/* patch starts ere */ + if (urb->dev->speed != USB_SPEED_HIGH) { + + cancel_qtd = td_ptd_map->qtd; + if (!qh || !cancel_qtd) { + err("Never Error:QH and QTD must not be zero\n"); + } else { + cancel_urb = cancel_qtd->urb; + cancel_urb_priv = + (urb_priv_t *) cancel_urb->hcpriv; + mem_addr = &cancel_qtd->mem_addr; + qha = &atlqha; + memset(qha, 0, sizeof(struct _isp1763_qha)); + + skipmap = + isp1763_reg_read16(hcd->dev, + hcd->regs. + atltdskipmap, + skipmap); + skipmap |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, + hcd->regs.atltdskipmap, + skipmap); + + /*read this ptd from the ram address,address is in the + td_ptd_map->ptd_header_addr */ + isp1763_mem_read(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, + 0); + if ((qha->td_info1 & QHA_VALID) + || (qha->td_info4 & QHA_ACTIVE)) { + + qha->td_info2 |= 0x00008000; + qha->td_info1 |= QHA_VALID; + qha->td_info4 |= QHA_ACTIVE; + skipmap &= ~td_ptd_map->ptd_bitmap; + ormask |= td_ptd_map->ptd_bitmap; + isp1763_reg_write16(hcd->dev, + hcd->regs. + atl_irq_mask_or, + ormask); + /* copy back into the header, payload is already + * present no need to write again */ + isp1763_mem_write(hcd->dev, + td_ptd_map-> + ptd_header_addr, 0, + (u32 *) (qha), + PHCI_QHA_LENGTH, 0); + /*unskip this td */ + isp1763_reg_write16(hcd->dev, + hcd->regs. + atltdskipmap, + skipmap); + udelay(100); + } + + isp1763_mem_read(hcd->dev, + td_ptd_map->ptd_header_addr, 0, + (u32 *) (qha), PHCI_QHA_LENGTH, + 0); + if (!(qha->td_info1 & QHA_VALID) + && !(qha->td_info4 & QHA_ACTIVE)) { + printk(KERN_NOTICE + "ptd has been retired \n"); + } + + } + } + +/* Patch Ends */ + /* These TDs are not pending anymore */ + td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap; + + /*tell atl worker this urb is going to be removed */ + td_ptd_map->state = TD_PTD_REMOVE; + /* These TDs are not pending anymore */ + td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap; + /*tell atl worker this urb is going to be removed */ + td_ptd_map->state = TD_PTD_REMOVE; + urb_priv->state |= DELETE_URB; + + /*read the skipmap, to see if this transfer has to be rescheduled */ + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.atltdskipmap, + skipmap); + pehci_check("remove skip map %x, ptd map %x\n", skipmap, + td_ptd_map->ptd_bitmap); + + buffstatus = + isp1763_reg_read16(hcd->dev, hcd->regs.buffer_status, + buffstatus); + + + isp1763_reg_write16(hcd->dev, hcd->regs.atltdskipmap, + skipmap | td_ptd_map->ptd_bitmap); + + while (!(skipmap & td_ptd_map->ptd_bitmap)) { + udelay(125); + + skipmap = isp1763_reg_read16(hcd->dev, + hcd->regs.atltdskipmap, + skipmap); + } + + /* if all transfers skipped, + * then disable the atl buffer, + * so that new transfer can come in + * need to see the side effects + * */ + if (skipmap == NO_TRANSFER_ACTIVE) { + /*disable the buffer */ + pehci_info("disable the atl buffer\n"); + buffstatus &= ~ATL_BUFFER; + isp1763_reg_write16(hcd->dev, hcd->regs.buffer_status, + buffstatus); + } + + qtd_list = &qh->qtd_list; + /*this should remove all pending transfers */ + pehci_check("num tds %d, urb length %d,device %d\n", + urb_priv->length, urb->transfer_buffer_length, + urb->dev->devnum); + + pehci_check("remove first qtd address %p\n", urb_priv->qtd[0]); + pehci_check("length of the urb %d, completed %d\n", + urb->transfer_buffer_length, urb->actual_length); + qtd = urb_priv->qtd[urb_priv->length - 1]; + pehci_check("qtd state is %x\n", qtd->state); + + + urb->status=status; + status = 0; +#ifdef USBNET +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map, NULL); +#else + pehci_hcd_urb_delayed_complete(hcd, qh, urb, td_ptd_map); +#endif +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL); +#else + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map); +#endif + +#endif + break; + + case PIPE_INTERRUPT: + pehci_check("phci_1763_urb_dequeue: INTR needs to be done\n"); + urb->status = status; //-ENOENT;//This will allow to suspend the system. in auto suspend mode + status = 0; + qh = urb_priv->qh; + if(qh==NULL) + break; + + td_ptd_buf = &td_ptd_map_buff[TD_PTD_BUFF_TYPE_INTL]; + td_ptd_map = &td_ptd_buf->map_list[qh->qtd_ptd_index]; + + /*urb is already been removed */ + if (td_ptd_map->state == TD_PTD_NEW) { + kfree(urb_priv); + break; + } + + /* These TDs are not pending anymore */ + td_ptd_buf->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap; + + td_ptd_map->state = TD_PTD_REMOVE; + urb_priv->state |= DELETE_URB; + + /*read the skipmap, to see if this transfer has to be rescheduled */ + skipmap = + isp1763_reg_read16(hcd->dev, hcd->regs.inttdskipmap, + skipmap); + + isp1763_reg_write16(hcd->dev, hcd->regs.inttdskipmap, + skipmap | td_ptd_map->ptd_bitmap); + qtd_list = &qh->qtd_list; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map, NULL); +#else + pehci_hcd_urb_complete(hcd, qh, urb, td_ptd_map); +#endif + break; +#ifdef CONFIG_ISO_SUPPORT + case PIPE_ISOCHRONOUS: + pehci_info("urb dequeue %x %x\n", urb,urb->pipe); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + if(urb->dev->speed==USB_SPEED_HIGH){ + retval = usb_hcd_check_unlink_urb(usb_hcd, urb, status); + if (!retval) { + pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status); + usb_hcd_unlink_urb_from_ep(usb_hcd, urb); + + + } + } +#endif + + + status = 0; + ep=urb->ep; + spin_unlock_irqrestore(&hcd->lock, flags); + mdelay(100); + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + if (urb->hcpriv!= periodic_ep[0]){ +#else + if (urb->ep != periodic_ep[0]){ +#endif + if(!list_empty(&ep->urb_list)){ + while(!list_empty(&ep->urb_list)){ + urb=container_of(ep->urb_list.next,struct urb,urb_list); + pehci_info("list is not empty %x %x\n",urb,urb->dev->state); + if(urb){ + retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0); + if (!retval) { + pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status); + usb_hcd_unlink_urb_from_ep(usb_hcd, urb); + } + urb->status=-ESHUTDOWN; + #if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(usb_hcd,urb); + #else + usb_hcd_giveback_urb(usb_hcd,urb,urb->status); + #endif + + } + } + }else{ + if(urb){ + pehci_info("list empty %x\n",urb->dev->state); + phcd_clean_urb_pending(hcd, urb); + retval = usb_hcd_check_unlink_urb(usb_hcd, urb,0); + if (!retval) { + pehci_info("[pehci_hcd_urb_dequeue] usb_hcd_unlink_urb_from_ep with status = %d\n", status); + usb_hcd_unlink_urb_from_ep(usb_hcd, urb); + } + urb->status=-ESHUTDOWN; + #if LINUX_VERSION_CODE <KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(usb_hcd,urb); + #else + usb_hcd_giveback_urb(usb_hcd,urb,urb->status); + #endif + + } + + } + } +#endif + return 0; + /*nothing to do here, wait till all transfers are done in iso worker */ + break; + } + + spin_unlock_irqrestore(&hcd->lock, flags); + pehci_info("status %d\n", status); + pehci_entry("-- %s: Exit\n", __FUNCTION__); + return status; +} + +/* bulk qh holds the data toggle */ + +static void +pehci_hcd_endpoint_disable(struct usb_hcd *usb_hcd, + struct usb_host_endpoint *ep) +{ + phci_hcd *ehci = usb_hcd_to_pehci_hcd(usb_hcd); + struct urb *urb; + + unsigned long flags; + struct ehci_qh *qh; + + pehci_entry("++ %s: Entered\n", __FUNCTION__); + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +#ifdef CONFIG_ISO_SUPPORT + mdelay(100); //delay for ISO +#endif + spin_lock_irqsave(&ehci->lock, flags); + + qh = ep->hcpriv; + + if (!qh) { + goto done; + } else { +#ifdef CONFIG_ISO_SUPPORT + pehci_info("disable endpoint %x %x\n", ep->desc.bEndpointAddress,qh->type); + + + if (qh->type == TD_PTD_BUFF_TYPE_ISTL) { + + /*wait for urb to get complete*/ + pehci_info("disable %x \n", list_empty(&ep->urb_list)); + while (!list_empty(&ep->urb_list)) { + + urb = container_of(ep->urb_list.next, + struct urb, urb_list); + if (urb) { + phcd_clean_urb_pending(ehci, urb); + spin_unlock_irqrestore(&ehci->lock, + flags); + + urb->status = -ESHUTDOWN; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + usb_hcd_giveback_urb(usb_hcd, urb); +#else + usb_hcd_giveback_urb(usb_hcd, urb, + urb->status); +#endif + spin_lock_irqsave(&ehci->lock, flags); + + } + + } + } +#endif + /*i will complete whatever left on this endpoint */ + pehci_complete_device_removal(ehci, qh); +#ifdef CONFIG_ISO_SUPPORT + phcd_clean_periodic_ep(); +#endif + ep->hcpriv = NULL; + + goto done; + } + done: + + ep->hcpriv = NULL; + + spin_unlock_irqrestore(&ehci->lock, flags); + printk("disable endpoint exit\n"); + pehci_entry("-- %s: Exit\n", __FUNCTION__); + return; +} + +/*called by core, for current frame number*/ +static int +pehci_hcd_get_frame_number(struct usb_hcd *usb_hcd) +{ + u32 framenumber = 0; + phci_hcd *pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + framenumber = + isp1763_reg_read16(pehci_hcd->dev, pehci_hcd->regs.frameindex, + framenumber); + return framenumber; +} + +/*root hub status data, called by root hub timer + *return 0, if no change, else + * 1, incase of high speed device + */ +static int +pehci_rh_status_data(struct usb_hcd *usb_hcd, char *buf) +{ + + u32 temp = 0, status = 0; + u32 ports = 0, i, retval = 1; + unsigned long flags; + phci_hcd *hcd = usb_hcd_to_pehci_hcd(usb_hcd); + + if (hcdpowerdown == 1) + return 0; + + buf[0] = 0; + if(portchange==1){ + printk("Remotewakeup-enumerate again \n"); + buf[0] |= 2; + hcd->reset_done[0] = 0; + return 1; + } + /* init status to no-changes */ + buf[0] = 0; + /*number of ports */ + ports = 0x1; + spin_lock_irqsave(&hcd->lock, flags); + /*read the port status registers */ + for (i = 0; i < ports; i++) { + temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[i], temp); + if (temp & PORT_OWNER) { + /* dont report the port status change in case of CC HCD + * but clear the port status , if there is any*/ + if (temp & PORT_CSC) { + temp &= ~PORT_CSC; + isp1763_reg_write32(hcd->dev, + hcd->regs.ports[i], temp); + continue; + } + } + + if (!(temp & PORT_CONNECT)) { + hcd->reset_done[i] = 0; + } + if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { + if (i < 7) { + buf[0] |= 1 << (i + 1); + } else { + buf[1] |= 1 << (i - 7); + } + status = STS_PCD; + } + } + + spin_unlock_irqrestore(&hcd->lock, flags); + return status ? retval : 0; +} + +/*root hub control requests*/ +static int +pehci_rh_control(struct usb_hcd *usb_hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + u32 ports = 0; + u32 temp = 0, status; + unsigned long flags; + int retval = 0; + phci_hcd *hcd = usb_hcd_to_pehci_hcd(usb_hcd); + + ports = 0x11; + + printk("%s: request %x,wValuse:0x%x, wIndex:0x%x \n",__func__, typeReq,wValue,wIndex); + + spin_lock_irqsave(&hcd->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + pehci_print("ClearPortFeature:0x%x\n", ClearPortFeature); + if (!wIndex || wIndex > (ports & 0xf)) { + pehci_info + ("ClearPortFeature not valid port number %d, should be %d\n", + wIndex, (ports & 0xf)); + goto error; + } + wIndex--; + temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex], + temp); + if (temp & PORT_OWNER) { + printk("port is owned by the CC host\n"); + break; + } + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + pehci_print("enable the port\n"); + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp & ~PORT_PE); + + break; + case USB_PORT_FEAT_C_ENABLE: + printk("disable the port\n"); + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp | PORT_PEC); + break; + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_C_SUSPEND: + printk("clear feature suspend \n"); + break; + case USB_PORT_FEAT_POWER: + if (ports & 0x10) { /*port has has power control switches */ + isp1763_reg_write32(hcd->dev, + hcd->regs.ports[wIndex], + temp & ~PORT_POWER); + } + break; + case USB_PORT_FEAT_C_CONNECTION: + pehci_print("connect change, status is 0x%08x\n", temp); + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp | PORT_CSC); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp | PORT_OCC); + break; + default: + goto error; + + } + break; + + case GetHubDescriptor: + pehci_hub_descriptor(hcd, (struct usb_hub_descriptor *) buf); + break; + + case GetHubStatus: + pehci_print("GetHubStatus:0x%x\n", GetHubStatus); + /* no hub-wide feature/status flags */ + memset(buf, 0, 4); + break; + case GetPortStatus: + pehci_print("GetPortStatus:0x%x\n", GetPortStatus); + if (!wIndex || wIndex > (ports & 0xf)) { + pehci_info + ("GetPortStatus,not valid port number %d, should be %d\n", + wIndex, (ports & 0xf)); + goto error; + } + wIndex--; + status = 0; + temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex], + temp); + printk("root port status:0x%x\n", temp); + /*connect status chnage */ + if (temp & PORT_CSC) { + status |= 1 << USB_PORT_FEAT_C_CONNECTION; + pehci_print("feature CSC 0x%08x and status 0x%08x \n", + temp, status); + } + if(portchange){ + portchange=0; + status |= 1 << USB_PORT_FEAT_C_CONNECTION; + } + /*port enable change */ + if (temp & PORT_PEC) { + status |= 1 << USB_PORT_FEAT_C_ENABLE; + pehci_print("feature PEC 0x%08x and status 0x%08x \n", + temp, status); + } + /*port over-current */ + if (temp & PORT_OCC) { + status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + pehci_print("feature OCC 0x%08x and status 0x%08x \n", + temp, status); + } + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) && jiffies > hcd->reset_done[wIndex]) { + status |= 1 << USB_PORT_FEAT_C_RESET; + pehci_print("feature reset 0x%08x and status 0x%08x\n", + temp, status); + printk(KERN_NOTICE + "feature reset 0x%08x and status 0x%08x\n", temp, + status); + /* force reset to complete */ + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp & ~PORT_RESET); + do { + mdelay(20); + temp = isp1763_reg_read32(hcd->dev, + hcd->regs. + ports[wIndex], temp); + } while (temp & PORT_RESET); + + /* see what we found out */ + printk(KERN_NOTICE "after portreset: %x\n", temp); + + temp = phci_check_reset_complete(hcd, wIndex, temp); + printk(KERN_NOTICE "after checkportreset: %x\n", temp); + } + + /* don't show wPortStatus if it's owned by a companion hc */ + + if (!(temp & PORT_OWNER)) { + + if (temp & PORT_CONNECT) { + status |= 1 << USB_PORT_FEAT_CONNECTION; + status |= 1 << USB_PORT_FEAT_HIGHSPEED; + } + if (temp & PORT_PE) { + status |= 1 << USB_PORT_FEAT_ENABLE; + } + if (temp & PORT_SUSPEND) { + status |= 1 << USB_PORT_FEAT_SUSPEND; + } + if (temp & PORT_OC) { + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + } + if (temp & PORT_RESET) { + status |= 1 << USB_PORT_FEAT_RESET; + } + if (temp & PORT_POWER) { + status |= 1 << USB_PORT_FEAT_POWER; + } + } + + /* This alignment is good, caller used kmalloc() */ + *((u32 *) buf) = cpu_to_le32(status); + break; + + case SetHubFeature: + pehci_print("SetHubFeature:0x%x\n", SetHubFeature); + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + pehci_print("SetPortFeature:%x\n", SetPortFeature); + if (!wIndex || wIndex > (ports & 0xf)) { + pehci_info + ("SetPortFeature not valid port number %d, should be %d\n", + wIndex, (ports & 0xf)); + goto error; + } + wIndex--; + temp = isp1763_reg_read32(hcd->dev, hcd->regs.ports[wIndex], + temp); + pehci_print("SetPortFeature:PortSc Val 0x%x\n", temp); + if (temp & PORT_OWNER) { + break; + } + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + /*enable the port */ + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp | PORT_PE); + break; + case USB_PORT_FEAT_SUSPEND: + + #if 0 /* Port suspend will be added in suspend function */ + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp | PORT_SUSPEND); + #endif + + break; + case USB_PORT_FEAT_POWER: + pehci_print("Set Port Power 0x%x and Ports %x\n", + USB_PORT_FEAT_POWER, ports); + if (ports & 0x10) { + printk(KERN_NOTICE + "PortSc Reg %x an Value %x\n", + hcd->regs.ports[wIndex], + (temp | PORT_POWER)); + + isp1763_reg_write32(hcd->dev, + hcd->regs.ports[wIndex], + temp | PORT_POWER); + } + break; + case USB_PORT_FEAT_RESET: + pehci_print("Set Port Reset 0x%x\n", + USB_PORT_FEAT_RESET); + if ((temp & (PORT_PE | PORT_CONNECT)) == PORT_CONNECT + && PORT_USB11(temp)) { + printk("error:port %d low speed --> companion\n", wIndex + 1); + temp |= PORT_OWNER; + } else { + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + hcd->reset_done[wIndex] = jiffies + + ((50 /* msec */ * HZ) / 1000); + } + isp1763_reg_write32(hcd->dev, hcd->regs.ports[wIndex], + temp); + break; + default: + goto error; + } + break; + default: + pehci_print("this request doesnt fit anywhere\n"); + error: + /* "stall" on error */ + pehci_info + ("unhandled root hub request: typereq 0x%08x, wValue %d, wIndex %d\n", + typeReq, wValue, wIndex); + retval = -EPIPE; + } + + pehci_info("rh_control:exit\n"); + spin_unlock_irqrestore(&hcd->lock, flags); + return retval; +} + + + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver pehci_driver = { + .description = hcd_name, + .product_desc = "ST-ERICSSON ISP1763", + .hcd_priv_size = sizeof(phci_hcd), + .irq = NULL, + + /* + * generic hardware linkage + */ + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = pehci_hcd_reset, + .start = pehci_hcd_start, + .bus_suspend = pehci_bus_suspend, + .bus_resume = pehci_bus_resume, + .stop = pehci_hcd_stop, + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = pehci_hcd_urb_enqueue, + .urb_dequeue = pehci_hcd_urb_dequeue, + .endpoint_disable = pehci_hcd_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = pehci_hcd_get_frame_number, + + /* + * root hub support + */ + .hub_status_data = pehci_rh_status_data, + .hub_control = pehci_rh_control, +}; + +/*probe the PCI host*/ + +#ifdef THREAD_BASED +int pehci_hcd_process_irq_it_handle(struct usb_hcd* usb_hcd_) +{ + int istatus; + + struct usb_hcd *usb_hcd; + char uIntStatus; + phci_hcd *pehci_hcd; + + struct list_head *pos, *lst_tmp; + st_UsbIt_Msg_Struc *mess; + unsigned long flags; + + g_stUsbItThreadHandler.phThreadTask = current; + siginitsetinv(&((g_stUsbItThreadHandler.phThreadTask)->blocked), sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); + pehci_info("pehci_hcd_process_irq_it_thread ID : %d\n", g_stUsbItThreadHandler.phThreadTask->pid); + + while (1) + { + if (signal_pending(g_stUsbItThreadHandler.phThreadTask)) + { + printk("thread handler: Thread received signal\n"); + break; + } + + spin_lock(&g_stUsbItThreadHandler.lock); + g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0; + spin_unlock(&g_stUsbItThreadHandler.lock); + + /* Wait until a signal arrives or we are woken up or timeout (5second)*/ + istatus = wait_event_interruptible_timeout(g_stUsbItThreadHandler.ulThrdWaitQhead, (g_stUsbItThreadHandler.lThrdWakeUpNeeded== 1), msecs_to_jiffies(MSEC_INTERVAL_CHECKING)); + + local_irq_save(flags); /*disable interrupt*/ + spin_lock(&g_stUsbItThreadHandler.lock); + g_stUsbItThreadHandler.lThrdWakeUpNeeded = 1; + spin_unlock(&g_stUsbItThreadHandler.lock); + //receive mess + if (!list_empty(&g_messList.list)) //mess list not empty + { + + list_for_each_safe(pos, lst_tmp, &(g_messList.list)) + { + mess = list_entry(pos, st_UsbIt_Msg_Struc, list); + + usb_hcd = mess->usb_hcd; + uIntStatus = mess->uIntStatus; + //pehci_print("-------------receive mess : %d------------\n",uIntStatus); + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + if((uIntStatus & NO_SOF_REQ_IN_TSK) || (uIntStatus & NO_SOF_REQ_IN_ISR) || (uIntStatus & NO_SOF_REQ_IN_REQ)) + pehci_interrupt_handler(pehci_hcd); + spin_lock(&g_stUsbItThreadHandler.lock); + list_del(pos); + kfree(mess); + spin_unlock(&g_stUsbItThreadHandler.lock); + } + } + else if(!list_empty(&g_enqueueMessList.list)) + { + mess = list_first_entry(&(g_enqueueMessList.list), st_UsbIt_Msg_Struc, list); + usb_hcd = mess->usb_hcd; + uIntStatus = mess->uIntStatus; + + pehci_print("-------------receive mess : %d------------\n",uIntStatus); + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + if((uIntStatus & NO_SOF_REQ_IN_REQ)) + { + pehci_interrupt_handler(pehci_hcd); + } + + { + spin_lock(&enqueue_lock); + list_del((g_enqueueMessList.list).next); + kfree(mess); + spin_unlock(&enqueue_lock); + } + } + else if(istatus == 0) //timeout + { + pehci_hcd = NULL; + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd_); + pehci_interrupt_handler(pehci_hcd); + + } + local_irq_restore(flags); /*enable interrupt*/ + } + + flush_signals(g_stUsbItThreadHandler.phThreadTask); + g_stUsbItThreadHandler.phThreadTask = NULL; + return 0; + +} + +int pehci_hcd_process_irq_in_thread(struct usb_hcd* usb_hcd_) +{ + + //status = msgq_create("usb_it_queue", 10, sizeof(st_UsbIt_Msg_Struc), &uUsbIt_MsgQueId); + INIT_LIST_HEAD(&g_messList.list); + INIT_LIST_HEAD(&g_enqueueMessList.list); + spin_lock_init(&enqueue_lock); + + memset(&g_stUsbItThreadHandler, 0, sizeof(st_UsbIt_Thread)); + init_waitqueue_head(&(g_stUsbItThreadHandler.ulThrdWaitQhead)); + g_stUsbItThreadHandler.lThrdWakeUpNeeded = 0; + spin_lock_init(&g_stUsbItThreadHandler.lock); + kernel_thread(pehci_hcd_process_irq_it_handle, usb_hcd_, 0); + + return 0; +} +#endif + + +/*probe the PCI host*/ +int +pehci_hcd_probe(struct isp1763_dev *isp1763_dev, isp1763_id * ids) +{ +#ifdef NON_PCI + struct platform_device *dev = isp1763_dev->dev; +#else /* PCI */ + struct pci_dev *dev = isp1763_dev->pcidev; +#endif + struct usb_hcd *usb_hcd; + phci_hcd *pehci_hcd; + int status = 0; + +#ifndef NON_PCI + u32 intcsr=0; +#endif + u16 wvalue1, wvalue2; + pehci_entry("++ %s: Entered\n", __FUNCTION__); + if (usb_disabled()) { + return -ENODEV; + } + + usb_hcd = usb_create_hcd(&pehci_driver,&dev->dev, "ISP1763"); + + if (usb_hcd == NULL) { + status = -ENOMEM; + goto clean; + } + + /* this is our host */ + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + pehci_hcd->dev = isp1763_dev; + pehci_hcd->iobase = (u8 *) isp1763_dev->baseaddress; + pehci_hcd->iolength = isp1763_dev->length; + pehci_hcd->plxiobase = (u8 *) isp1763_dev->dmabase; + pehci_hcd->plxiolength = isp1763_dev->length; + + + /* lets keep our host here */ + isp1763_dev->driver_data = usb_hcd; +#ifdef NON_PCI +//Do nothing +#else + /* Enable the interrupts from PLX to PCI */ + /* CONFIGURE PCI/PLX interrupt */ +#ifdef DATABUS_WIDTH_16 + wvalue1 = readw(pehci_hcd->plxiobase + 0x68); + wvalue2 = readw(pehci_hcd->plxiobase + 0x68 + 2); + intcsr |= wvalue2; + intcsr <<= 16; + intcsr |= wvalue1; + printk(KERN_NOTICE "Enable PCI Intr: %x \n", intcsr); + intcsr |= 0x900; + writew((u16) intcsr, pehci_hcd->plxiobase + 0x68); + writew((u16) (intcsr >> 16), pehci_hcd->plxiobase + 0x68 + 2); +#else + bvalue1 = readb(pehci_hcd->plxiobase + 0x68); + bvalue2 = readb(pehci_hcd->plxiobase + 0x68 + 1); + bvalue3 = readb(pehci_hcd->plxiobase + 0x68 + 2); + bvalue4 = readb(pehci_hcd->plxiobase + 0x68 + 3); + intcsr |= bvalue4; + intcsr <<= 8; + intcsr |= bvalue3; + intcsr <<= 8; + intcsr |= bvalue2; + intcsr <<= 8; + intcsr |= bvalue1; + writeb((u8) intcsr, pehci_hcd->plxiobase + 0x68); + writeb((u8) (intcsr >> 8), pehci_hcd->plxiobase + 0x68 + 1); + writeb((u8) (intcsr >> 16), pehci_hcd->plxiobase + 0x68 + 2); + writeb((u8) (intcsr >> 24), pehci_hcd->plxiobase + 0x68 + 3); +#endif +#endif + + No_Data_Phase = 0; + No_Status_Phase = 0; + usb_hcd->self.controller->dma_mask = 0; + usb_hcd->self.otg_port = 1; + #ifndef THREAD_BASED + status = isp1763_request_irq((void*)pehci_hcd_irq, isp1763_dev, usb_hcd); + #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + if (status == 0) { + status = usb_add_hcd(usb_hcd, isp1763_dev->irq, SA_SHIRQ); + } +#else /* Linux 2.6.28*/ + usb_hcd->self.uses_dma = 0; + if (status == 0){ + status = usb_add_hcd(usb_hcd, isp1763_dev->irq, + IRQF_SHARED | IRQF_DISABLED); + } +#endif + +#ifdef THREAD_BASED + g_pehci_hcd = pehci_hcd; +#endif + +#ifdef USBNET + // initialize clean up urb list + INIT_LIST_HEAD(&(pehci_hcd->cleanup_urb.urb_list)); +#endif + + pehci_entry("-- %s: Exit\n", __FUNCTION__); + isp1763_hcd=isp1763_dev; + return status; + + clean: + return status; + +} +/*--------------------------------------------------------------* + * + * Module details: pehci_hcd_powerup + * + * This function powerdown the chip completely, which make chip works in minimal power + * + * Input: struct isp1763_Dev * + * + * + * + * + * Called by: IOCTL function + * + * + --------------------------------------------------------------*/ +void +pehci_hcd_powerup(struct isp1763_dev *dev) +{ + printk("%s\n", __FUNCTION__); + hcdpowerdown = 0; + dev->driver->probe(dev,dev->driver->id); + + +} +void +pehci_hcd_powerdown(struct isp1763_dev *dev) +{ + struct usb_hcd *usb_hcd; + + phci_hcd *hcd = NULL; + u32 temp; + usb_hcd = (struct usb_hcd *) dev->driver_data; + if (!usb_hcd) { + return; + } + + printk("%s\n", __FUNCTION__); + hcd = usb_hcd_to_pehci_hcd(usb_hcd); + + temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0); + temp &= ~0x01; /* stop the controller first */ + isp1763_reg_write16(dev, HC_USBCMD_REG, temp); + printk("++ %s: Entered\n", __FUNCTION__); + + isp1763_free_irq(dev,usb_hcd); + usb_remove_hcd(usb_hcd); + dev->driver_data = NULL; + + + temp = isp1763_reg_read16(dev, HC_INTENABLE_REG, temp); //0xD6 + temp &= ~0x400; /*disable otg interrupt*/ + isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6 + + isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37); /*unlock the device 0x7c*/ + mdelay(1); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + + + if ((temp & 0x1005) == 0x1005) { + isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1000); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + mdelay(10); + isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1104); + mdelay(10); + isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1007); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + mdelay(10); + isp1763_reg_write32(dev, HC_PORTSC1_REG, 0x1005); + + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + } + + printk("port status %x\n ", temp); + temp &= ~0x2; + temp &= ~0x40; /*force port resume*/ + temp |= 0x80; /*suspend*/ + + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + printk("port status %x\n ", temp); + mdelay(200); + + temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); /*suspend the device first 0xc*/ + temp |= 0x2c; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc + mdelay(20); + + temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); //0xc + temp = 0xc; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc + + isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0800); + + hcdpowerdown = 1; + +} + +static int pehci_bus_suspend(struct usb_hcd *usb_hcd) +{ + u32 temp=0; + unsigned long flags; + phci_hcd *pehci_hcd = NULL; + struct isp1763_dev *dev = NULL; + + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + + dev = pehci_hcd->dev; + + if (!usb_hcd) { + return -EBUSY; + } + + printk("++ %s \n",__FUNCTION__); + + if(hcdpowerdown){ + return 0; + } + + spin_lock_irqsave(&pehci_hcd->lock, flags); + + isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90 + isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94 + isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4 + + temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4 + + isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK); + temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0); + + hcdpowerdown = 1; + + /* stop the controller first */ + temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0); + temp &= ~0x01; + isp1763_reg_write16(dev, HC_USBCMD_REG, temp); + + /* suspend root port which will suspend host controller of the ISP1763A */ + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + temp |= (PORT_SUSPEND);//0x80 + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + + /* suspend device controller of the ISP1763a*/ + temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); + temp |= 0x20; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); + mdelay(1); // make sure there will not be huge delay here max is 1 ms + temp &= ~0x20; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); + /* put host controoler into low power mode */ + isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_SUSPEND_VALUE); + +// usb_hcd->state = HC_STATE_SUSPENDED; + + spin_unlock_irqrestore(&pehci_hcd->lock, flags); + + printk("-- %s \n",__FUNCTION__); + + return 0; + + +} + +static int pehci_bus_resume(struct usb_hcd *usb_hcd) +{ + u32 temp,i; + phci_hcd *pehci_hcd = NULL; + struct isp1763_dev *dev = NULL; + unsigned long flags; + u32 usbsts, portsc1; + u32 int_reg=0, irq_mask; + + printk("%s Enter \n",__func__); + + if (!usb_hcd) { + return -EBUSY; + } + + if(hcdpowerdown ==0){ + printk("%s already executed\n ",__func__); + return 0; + } + + pehci_hcd = usb_hcd_to_pehci_hcd(usb_hcd); + dev = pehci_hcd->dev; + spin_lock_irqsave(&pehci_hcd->lock, flags); + + for (temp = 0; temp < 100; temp++) + { + i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0); + if(i==0x176320) + break; + mdelay(2); + } + printk("temp=%d, chipid:0x%x \n",temp,i); + mdelay(10); + isp1763_reg_write16(dev, HC_UNLOCK_DEVICE, 0xAA37); /*unlock the device 0x7c*/ + i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0); + printk("POWER DOWN CTRL REG value during suspend =0x%x\n", i); + for (temp = 0; temp < 100; temp++) { + mdelay(1); + isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE); + mdelay(1); + i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0); + if(i==POWER_DOWN_CTRL_NORMAL_VALUE) + break; + } + if (temp == 100) { + spin_unlock_irqrestore(&pehci_hcd->lock, flags); + pr_err("%s:isp1763a failed to resume\n", __func__); + return -1; + } + printk("%s: Powerdown Reg Val: 0x%08x -- %d\n", __func__, i, temp); + + isp1763_reg_write32(dev, HC_USBSTS_REG,0x0); //0x90 + isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x0); //0x94 + isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6 + + portsc1 = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + printk("%s USBSTS: 0x%x, PORTSC1: 0x%x, INTSTATUS: 0x%x, INTMASK: 0x%x\n", + __func__, usbsts, portsc1, int_reg, irq_mask); + + temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0); + temp |= 0x01; /* Start the controller */ + isp1763_reg_write16(dev, HC_USBCMD_REG, temp); + mdelay(10); + + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + if (temp & PORT_SUSPEND) + pr_err("%s: HC_PORTSC1_REG: 0x%08x\n", __func__, temp); + temp |= PORT_SUSPEND; //0x80; + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + mdelay(50); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + temp |= PORT_RESUME; //0x40; + temp &= ~(PORT_SUSPEND); //0x80; /*suspend*/ + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + temp &= ~(PORT_RESUME); //0x40; + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + + temp = INTR_ENABLE_MASK; + isp1763_reg_write16(dev, HC_INTENABLE_REG, temp); //0xD6 + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + printk("%s resume port status: 0x%x\n", __func__, temp); + if(!(temp & 0x4)){ //port is disabled + isp1763_reg_write16(dev, HC_INTENABLE_REG, 0x1005); //0xD6 + mdelay(10); + } +// phci_resume_wakeup(dev); + + hcdpowerdown = 0; + if(hubdev){ + hubdev->hcd_priv = NULL; + hubdev->hcd_suspend = NULL; + } + + spin_unlock_irqrestore(&pehci_hcd->lock, flags); + printk("%s Leave\n",__func__); + + return 0; +} + +void +pehci_hcd_resume(struct isp1763_dev *dev) +{ + struct usb_hcd *usb_hcd; + u32 temp,i; + usb_hcd = (struct usb_hcd *) dev->driver_data; + if (!usb_hcd) { + return; + } + + if(hcdpowerdown ==0){ + return ; + } + + printk("%s \n",__FUNCTION__); + + for (temp = 0; temp < 10; temp++) + { + i = isp1763_reg_read32(dev, HC_CHIP_ID_REG, 0); + printk("temp=%d, chipid:0x%x \n",temp,i); + if(i==0x176320) + break; + mdelay(1); + } + + /* Start the controller */ + temp = 0x01; + isp1763_reg_write16(dev, HC_USBCMD_REG, temp); + + /* update power down control reg value */ + for (temp = 0; temp < 100; temp++) { + isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, POWER_DOWN_CTRL_NORMAL_VALUE); + i = isp1763_reg_read32(dev, HC_POWER_DOWN_CONTROL_REG, 0); + if(i==POWER_DOWN_CTRL_NORMAL_VALUE) + break; + } + + if (temp == 100) { + pr_err("%s:isp1763a failed to resume\n", __func__); + return; + } + + isp1763_reg_write16(dev, HC_INTENABLE_REG,0); //0xD6 + isp1763_reg_write32(dev,HC_INTERRUPT_REG_EHCI,0x4); //0x94 + isp1763_reg_write32(dev,HC_INTERRUPT_REG,0xFFFF); //0x94 + /* clear suspend bit and resume bit */ + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + temp &= ~(PORT_SUSPEND); //0x80; /*suspend*/ + temp &= ~(PORT_RESUME); // 0x40; + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + + isp1763_reg_write16(dev, HC_INTENABLE_REG, INTR_ENABLE_MASK); //0xD6 + /*this is just make sure port is resumed back */ + mdelay(1); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + printk("after hcd resume :port status %x\n ", temp); + + hcdpowerdown = 0; + +#ifndef NON_PCI + phci_resume_wakeup(dev); +#endif + + if(hubdev){ + hubdev->hcd_priv=NULL; + hubdev->hcd_suspend=NULL; + } +// usb_hcd->state = HC_STATE_RUNNING; + +} + + +void +pehci_hcd_suspend(struct isp1763_dev *dev) +{ + struct usb_hcd *usb_hcd; + u32 temp; + usb_hcd = (struct usb_hcd *) dev->driver_data; + if (!usb_hcd) { + return; + } + printk("%s \n",__FUNCTION__); + if(hcdpowerdown){ + return ; + } + + temp = isp1763_reg_read16(dev, HC_USBCMD_REG, 0); + temp &= ~0x01; /* stop the controller first */ + isp1763_reg_write16(dev, HC_USBCMD_REG, temp); + + isp1763_reg_write32(dev, HC_USBSTS_REG, 0x4); //0x90 + isp1763_reg_write32(dev, HC_INTERRUPT_REG_EHCI, 0x4); //0x94 + isp1763_reg_write16(dev, HC_INTERRUPT_REG, INTR_ENABLE_MASK); //0xd4 + + temp=isp1763_reg_read16(dev, HC_INTERRUPT_REG, 0); //0xd4 + + printk("suspend :Interrupt Status %x\n",temp); + isp1763_reg_write16(dev,HC_INTENABLE_REG,INTR_ENABLE_MASK); + temp=isp1763_reg_read16(dev,HC_INTENABLE_REG,0); + printk("suspend :Interrupt Enable %x\n",temp); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + + printk("suspend :port status %x\n ", temp); + temp &= ~0x2; + temp &= ~0x40; /*force port resume*/ + temp |= 0x80; /*suspend*/ +// temp |= 0x700000; /*WKCNNT_E,WKDSCNNT_E,WKOC_E*/ + isp1763_reg_write32(dev, HC_PORTSC1_REG, temp); + // mdelay(10); + temp = isp1763_reg_read32(dev, HC_PORTSC1_REG, 0); + printk("suspend :port status %x\n ", temp); + hcdpowerdown = 1; + + + temp = isp1763_reg_read16(dev,HC_HW_MODE_REG, 0); /*suspend the device first 0xc*/ + temp&=0xff7b; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp); //0xc + + + temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0); /*suspend the device first 0xc*/ + temp |= 0x20; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc + mdelay(2); + temp = isp1763_reg_read16(dev, HC_HW_MODE_REG, 0);//0xc + temp &= 0xffdf; + temp &= ~0x20; + isp1763_reg_write16(dev, HC_HW_MODE_REG, temp);//0xc + + isp1763_reg_write32(dev, HC_POWER_DOWN_CONTROL_REG, 0xffff0830); + + +} + +void +pehci_hcd_remotewakeup(struct isp1763_dev *dev){ + if(hubdev){ + hubdev->hcd_priv=dev; + hubdev->hcd_suspend=(void *)pehci_hcd_suspend; + } + phci_remotewakeup(dev); +} + +/*remove the host controller*/ +static void +pehci_hcd_remove(struct isp1763_dev *isp1763_dev) +{ + + struct usb_hcd *usb_hcd; + +#ifdef NON_PCI +#else /* PCI */ +// struct pci_dev *dev = isp1763_dev->pcidev; +#endif + + phci_hcd *hcd = NULL; + u32 temp; + usb_hcd = (struct usb_hcd *) isp1763_dev->driver_data; + if (!usb_hcd) { + return; + } + hcd=usb_hcd_to_pehci_hcd(usb_hcd); + isp1763_reg_write32(hcd->dev,hcd->regs.hwmodecontrol,0); + isp1763_reg_write32(hcd->dev,hcd->regs.interruptenable,0); + hubdev=0; + huburb=0; + temp = isp1763_reg_read16(hcd->dev, HC_USBCMD_REG, 0); + temp &= ~0x01; /* stop the controller first */ + isp1763_reg_write16(hcd->dev, HC_USBCMD_REG, temp); + isp1763_free_irq(isp1763_dev,usb_hcd); + usb_remove_hcd(usb_hcd); + + return ; +} + + +static isp1763_id ids = { + .idVendor = 0x04CC, /*st ericsson isp1763 vendor_id */ + .idProduct = 0x1A64, /*st ericsson isp1763 product_id */ + .driver_info = (unsigned long) &pehci_driver, +}; + +/* pci driver glue; this is a "new style" PCI driver module */ +static struct isp1763_driver pehci_hcd_pci_driver = { + .name = (char *) hcd_name, + .index = 0, + .id = &ids, + .probe = pehci_hcd_probe, + .remove = pehci_hcd_remove, + .suspend = pehci_hcd_suspend, + .resume = pehci_hcd_resume, + .remotewakeup=pehci_hcd_remotewakeup, + .powerup = pehci_hcd_powerup, + .powerdown = pehci_hcd_powerdown, +}; + +#ifdef HCD_PACKAGE +int +usb_hcddev_open(struct inode *inode, struct file *fp) +{ + + return 0; +} + +int +usb_hcddev_close(struct inode *inode, struct file *fp) +{ + + return 0; +} + +int +usb_hcddev_fasync(int fd, struct file *fp, int mode) +{ + + return fasync_helper(fd, fp, mode, &fasync_q); +} + +int +usb_hcddev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + + switch (cmd) { + case HCD_IOC_POWERDOWN: /* SET HCD DEEP SUSPEND MODE */ + printk("HCD IOC POWERDOWN MODE\n"); + if(isp1763_hcd->driver->powerdown) + isp1763_hcd->driver->powerdown(isp1763_hcd); + + break; + + case HCD_IOC_POWERUP: /* Set HCD POWER UP */ + printk("HCD IOC POWERUP MODE\n"); + if(isp1763_hcd->driver->powerup) + isp1763_hcd->driver->powerup(isp1763_hcd); + + break; + case HCD_IOC_TESTSE0_NACK: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_TEST_SE0_NAK; + break; + case HCD_IOC_TEST_J: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_TEST_J; + break; + case HCD_IOC_TEST_K: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_TEST_K; + break; + + case HCD_IOC_TEST_TESTPACKET: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_TEST_PACKET; + break; + case HCD_IOC_TEST_FORCE_ENABLE: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_TEST_FORCE_ENABLE; + break; + case HCD_IOC_TEST_SUSPEND_RESUME: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME; + break; + case HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_SINGLE_STEP_GET_DEV_DESC; + break; + case HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE: + HostComplianceTest = HOST_COMPILANCE_TEST_ENABLE; + HostTest = HOST_COMP_SINGLE_STEP_SET_FEATURE; + break; + case HCD_IOC_TEST_STOP: + HostComplianceTest = 0; + HostTest = 0; + break; + case HCD_IOC_SUSPEND_BUS: + printk("isp1763:SUSPEND bus\n"); + if(isp1763_hcd->driver->suspend) + isp1763_hcd->driver->suspend(isp1763_hcd); + break; + case HCD_IOC_RESUME_BUS: + printk("isp1763:RESUME bus\n"); + if(isp1763_hcd->driver->resume) + isp1763_hcd->driver->resume(isp1763_hcd); + break; + case HCD_IOC_REMOTEWAKEUP_BUS: + printk("isp1763:SUSPEND bus\n"); + if(isp1763_hcd->driver->remotewakeup) + isp1763_hcd->driver->remotewakeup(isp1763_hcd); + break; + default: + + break; + + } + return 0; +} + +/* HCD file operations */ +static struct file_operations usb_hcddev_fops = { + owner:THIS_MODULE, + read:NULL, + write:NULL, + poll:NULL, + unlocked_ioctl:usb_hcddev_ioctl, + open:usb_hcddev_open, + release:usb_hcddev_close, + fasync:usb_hcddev_fasync, +}; + +#endif + + +static int __init +pehci_module_init(void) +{ + int result = 0; + phci_hcd_mem_init(); + + /*register driver */ + result = isp1763_register_driver(&pehci_hcd_pci_driver); + if (!result) { + info("Host Driver has been Registered"); + } else { + err("Host Driver has not been Registered with errors : %x", + result); + } + +#ifdef THREAD_BASED + pehci_hcd_process_irq_in_thread(&(g_pehci_hcd->usb_hcd)); + printk("kernel_thread() Enter\n"); +#endif + +#ifdef HCD_PACKAGE + printk("Register Char Driver for HCD\n"); + result = register_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME, + &usb_hcddev_fops); + +#endif + return result; + +} + +static void __exit +pehci_module_cleanup(void) +{ +#ifdef THREAD_BASED + printk("module exit: Sending signal to stop thread\n"); + if (g_stUsbItThreadHandler.phThreadTask != NULL) + { + send_sig(SIGKILL, g_stUsbItThreadHandler.phThreadTask, 1); + mdelay(6); + } +#endif + +#ifdef HCD_PACKAGE + unregister_chrdev(USB_HCD_MAJOR, USB_HCD_MODULE_NAME); +#endif + isp1763_unregister_driver(&pehci_hcd_pci_driver); +} + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +module_init(pehci_module_init); +module_exit(pehci_module_cleanup); |