diff options
-rw-r--r-- | drivers/usb/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 22 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-hal-interface.h | 312 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-hal.c | 1757 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-hal.h | 85 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-hcd.c | 6534 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-hcd.h | 755 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-itdptd.c | 2156 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-mem.c | 355 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-otg.c | 189 | ||||
-rw-r--r-- | drivers/usb/host/isp1763-qtdptd.c | 1315 | ||||
-rw-r--r-- | drivers/usb/host/isp1763.h | 224 |
13 files changed, 13708 insertions, 0 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 30ddf8dc4f7..31d46a1c342 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -21,6 +21,8 @@ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_ISP1760_HCD) += host/ +obj-$(CONFIG_USB_ISP1763_HAL) += host/ +obj-$(CONFIG_USB_ISP1763_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f12d57..d9d93699711 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -269,6 +269,28 @@ config USB_ISP1362_HCD To compile this driver as a module, choose M here: the module will be called isp1362-hcd. +config USB_ISP1763_HAL + tristate "ISP1763 HAL driver" + depends on USB + default N + ---help--- + ISP1763 Hardware Abstraction Layer driver. + + To compile this driver as a module, choose M here: the + module will be called isp1763-hal. + +config USB_ISP1763_HCD + tristate "ISP1763 Host Controller" + depends on USB && USB_ISP1763_HAL + default N + ---help--- + Supports for the ISP1763 chip as a host controller + + This driver does not support isochronous transfers well. + + To compile this driver as a module, choose M here: the + module will be called isp1763-hcd. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 624a362f2fe..7fbe65f1123 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -22,6 +22,8 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o +obj-$(CONFIG_USB_ISP1763_HAL) += isp1763-hal.o +obj-$(CONFIG_USB_ISP1763_HCD) += isp1763-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o diff --git a/drivers/usb/host/isp1763-hal-interface.h b/drivers/usb/host/isp1763-hal-interface.h new file mode 100644 index 00000000000..c19d6506a43 --- /dev/null +++ b/drivers/usb/host/isp1763-hal-interface.h @@ -0,0 +1,312 @@ +/*
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010
+*
+* ISP1763 Linux OTG Controller driver : hal
+*
+* 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.
+*
+* This is a hardware abstraction layer header file.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef HAL_INTF_H
+#define HAL_INTF_H
+
+
+#define HCD_PACKAGE
+
+#define NON_PCI
+#undef PXA300
+
+//#define MSEC_INT_BASED
+#ifdef MSEC_INT_BASED
+#define THREAD_BASED
+#endif
+
+//#ifndef DATABUS_WIDTH_16
+#define DATABUS_WIDTH_16
+//#endif
+
+#ifdef DATABUS_WIDTH_16
+/*DMA SUPPORT */
+//#define ENABLE_PLX_DMA
+#undef ENABLE_PLX_DMA
+#endif
+
+#define EDGE_INTERRUPT
+#define POL_HIGH_INTERRUPT
+
+#define DMA_BUF_SIZE (4096 * 2)
+
+#define ISP1763_CHIPID 0x176320
+
+/* Values for id_flags filed of isp1763_driver_t */
+#define ISP1763_HC 0 /* Host Controller Driver */
+#define ISP1763_DC 1 /* Device Controller Driver */
+#define ISP1763_OTG 2 /* Otg Controller Driver */
+#define ISP1763_LAST_DEV (ISP1763_OTG + 1)
+#define ISP1763_1ST_DEV (ISP1763_HC)
+
+#ifdef PXA300
+#define HC_SPARAMS_REG (0x04<<1) /* Structural Parameters Register */
+#define HC_CPARAMS_REG (0x08<<1) /* Capability Parameters Register */
+
+#define HC_USBCMD_REG (0x8C<<1) /* USB Command Register */
+#define HC_USBSTS_REG (0x90<<1) /* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI (0x94<<1) /* INterrupt Enable Register */
+#define HC_FRINDEX_REG (0x98<<1) /* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG (0x9C<<1) /* Conigured Flag Register */
+#define HC_PORTSC1_REG (0xA0<<1) /* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG (0xA4<<1) /* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG (0xA6<<1) /* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG (0xA8<<1) /* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG (0xAA<<1) /* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG (0xAC<<1) /* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG (0xAE<<1) /* INT PTD Last PTD Register */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG (0xB0<<1) /* ATL PTD Last PTD Register */
+#define HC_ATL_PTD_SKIPMAP_REG (0xB2<<1) /* ATL PTD Last PTD Register */
+#define HC_ATL_PTD_LASTPTD_REG (0xB4<<1) /* ATL PTD Last PTD Register */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG (0x0C<<1) /* H/W Mode Register */
+#define HC_CHIP_ID_REG (0x70<<1) /* Chip ID Register */
+#define HC_SCRATCH_REG (0x78<<1) /* Scratch Register */
+#define HC_RESET_REG (0xB8<<1) /* HC Reset Register */
+#define HC_HWMODECTRL_REG (0xB6<<1)
+#define HC_UNLOCK_DEVICE (0x7C<<1)
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG (0xD4<<1) /* Interrupt Register */
+#define HC_INTENABLE_REG (0xD6<<1) /* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG (0xD8<<1) /* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG (0xDA<<1) /* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG (0xDC<<1) /* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG (0xDE<<1) /* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG (0xE0<<1) /* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG (0xE2<<1) /* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG (0xD0<<1)
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG (0xBC<<1) /* DMA Config Register */
+#define HC_MEM_READ_REG (0xC4<<1) /* Memory Register */
+#define HC_DATA_REG (0xC6<<1) /* Data Register */
+
+#define OTG_CTRL_SET_REG (0xE4<<1)
+#define OTG_CTRL_CLEAR_REG (0xE6<<1)
+#define OTG_SOURCE_REG (0xE8<<1)
+
+#define OTG_INTR_EN_F_SET_REG (0xF0<<1)
+#define OTG_INTR_EN_R_SET_REG (0xF4<<1) /* OTG Interrupt Enable Rise register */
+
+#else
+#define HC_SPARAMS_REG 0x04 /* Structural Parameters Register */
+#define HC_CPARAMS_REG 0x08 /* Capability Parameters Register */
+
+#define HC_USBCMD_REG 0x8C /* USB Command Register */
+#define HC_USBSTS_REG 0x90 /* USB Status Register */
+#define HC_INTERRUPT_REG_EHCI 0x94 /* INterrupt Enable Register */
+#define HC_FRINDEX_REG 0x98 /* Frame Index Register */
+
+#define HC_CONFIGFLAG_REG 0x9C /* Conigured Flag Register */
+#define HC_PORTSC1_REG 0xA0 /* Port Status Control for Port1 */
+
+/*ISO Transfer Registers */
+#define HC_ISO_PTD_DONEMAP_REG 0xA4 /* ISO PTD Done Map Register */
+#define HC_ISO_PTD_SKIPMAP_REG 0xA6 /* ISO PTD Skip Map Register */
+#define HC_ISO_PTD_LASTPTD_REG 0xA8 /* ISO PTD Last PTD Register */
+
+/*INT Transfer Registers */
+#define HC_INT_PTD_DONEMAP_REG 0xAA /* INT PTD Done Map Register */
+#define HC_INT_PTD_SKIPMAP_REG 0xAC /* INT PTD Skip Map Register */
+#define HC_INT_PTD_LASTPTD_REG 0xAE /* INT PTD Last PTD Register */
+
+/*ATL Transfer Registers */
+#define HC_ATL_PTD_DONEMAP_REG 0xB0 /* ATL PTD Last PTD Register */
+#define HC_ATL_PTD_SKIPMAP_REG 0xB2 /* ATL PTD Last PTD Register */
+#define HC_ATL_PTD_LASTPTD_REG 0xB4 /* ATL PTD Last PTD Register */
+
+/*General Purpose Registers */
+#define HC_HW_MODE_REG 0x0C //0xB6 /* H/W Mode Register */
+#define HC_CHIP_ID_REG 0x70 /* Chip ID Register */
+#define HC_SCRATCH_REG 0x78 /* Scratch Register */
+#define HC_RESET_REG 0xB8 /* HC Reset Register */
+#define HC_HWMODECTRL_REG 0xB6 //0x0C /* H/W Mode control Register */
+#define HC_UNLOCK_DEVICE 0x7C
+
+/* Interrupt Registers */
+#define HC_INTERRUPT_REG 0xD4 /* Interrupt Register */
+#define HC_INTENABLE_REG 0xD6 /* Interrupt enable Register */
+#define HC_ISO_IRQ_MASK_OR_REG 0xD8 /* ISO Mask OR Register */
+#define HC_INT_IRQ_MASK_OR_REG 0xDA /* INT Mask OR Register */
+#define HC_ATL_IRQ_MASK_OR_REG 0xDC /* ATL Mask OR Register */
+#define HC_ISO_IRQ_MASK_AND_REG 0xDE /* ISO Mask AND Register */
+#define HC_INT_IRQ_MASK_AND_REG 0xE0 /* INT Mask AND Register */
+#define HC_ATL_IRQ_MASK_AND_REG 0xE2 /* ATL Mask AND Register */
+
+/*power control reg */
+#define HC_POWER_DOWN_CONTROL_REG 0xD0
+
+/*RAM Registers */
+#define HC_DMACONFIG_REG 0xBC /* DMA Config Register */
+#define HC_MEM_READ_REG 0xC4 /* Memory Register */
+#define HC_DATA_REG 0xC6 /* Data Register */
+
+#define OTG_CTRL_SET_REG 0xE4
+#define OTG_CTRL_CLEAR_REG 0xE6
+#define OTG_SOURCE_REG 0xE8
+
+#define OTG_INTR_EN_F_SET_REG 0xF0 /* OTG Interrupt Enable Fall register */
+#define OTG_INTR_EN_R_SET_REG 0xF4 /* OTG Interrupt Enable Rise register */
+
+#endif
+
+#define OTG_CTRL_DPPULLUP 0x0001
+#define OTG_CTRL_DPPULLDOWN 0x0002
+#define OTG_CTRL_DMPULLDOWN 0x0004
+#define OTG_CTRL_VBUS_DRV 0x0010
+#define OTG_CTRL_VBUS_DISCHRG 0x0020
+#define OTG_CTRL_VBUS_CHRG 0x0040
+#define OTG_CTRL_SW_SEL_HC_DC 0x0080
+#define OTG_CTRL_BDIS_ACON_EN 0x0100
+#define OTG_CTRL_OTG_SE0_EN 0x0200
+#define OTG_CTRL_OTG_DISABLE 0x0400
+#define OTG_CTRL_VBUS_DRV_PORT2 0x1000
+#define OTG_CTRL_SW_SEL_HC_2 0x8000
+
+/*interrupt count and buffer status register*/
+
+
+#ifdef PXA300
+#define HC_BUFFER_STATUS_REG (0xBA<<1)
+#define HC_INT_THRESHOLD_REG (0xC8<<1)
+#else
+#define HC_BUFFER_STATUS_REG 0xBA
+#define HC_INT_THRESHOLD_REG 0xC8
+#endif
+
+#define HC_OTG_INTERRUPT 0x400
+
+#ifdef PXA300
+#define DC_CHIPID (0x70<<1)
+#else
+#define DC_CHIPID 0x70
+#endif
+
+
+#ifdef PXA300
+#define FPGA_CONFIG_REG (0x100<<1)
+#else
+#define FPGA_CONFIG_REG 0x100
+#endif
+
+#define HC_HW_MODE_GOBAL_INTR_ENABLE 0x01
+#define HC_HW_MODE_INTR_EDGE 0x02
+#define HC_HW_MODE_INTR_POLARITY_HIGH 0x04
+#define HC_HW_MODE_LOCK 0x08
+#define HC_HW_MODE_DATABUSWIDTH_8 0x10
+#define HC_HW_MODE_DREQ_POL_HIGH 0x20
+#define HC_HW_MODE_DACK_POL_HIGH 0x40
+#define HC_HW_MODE_COMN_INT 0x80
+
+struct isp1763_driver;
+typedef struct _isp1763_id {
+ u16 idVendor;
+ u16 idProduct;
+ u32 driver_info;
+} isp1763_id;
+
+typedef struct isp1763_dev {
+ /*added for pci device */
+#ifdef NON_PCI
+ struct platform_device *dev;
+#else /*PCI*/
+ struct pci_dev *pcidev;
+#endif
+ struct isp1763_driver *driver; /* which driver has allocated this device */
+ void *driver_data; /* data private to the host controller driver */
+ void *otg_driver_data; /*data private for otg controler */
+ unsigned char index; /* local controller (HC/DC/OTG) */
+ unsigned int irq; /*Interrupt Channel allocated for this device */
+ void (*handler) (struct isp1763_dev * dev, void *isr_data); /* Interrupt Serrvice Routine */
+ void *isr_data; /* isr data of the driver */
+ unsigned long int_reg; /* Interrupt register */
+ unsigned long alt_int_reg; /* Interrupt register 2 */
+ unsigned long start;
+ unsigned long length;
+ struct resource *mem_res;
+ unsigned long io_base; /* Start Io address space for this device */
+ unsigned long io_len; /* IO address space length for this device */
+
+ unsigned long chip_id; /* Chip Id */
+
+ char name[80]; /* device name */
+ int active; /* device status */
+
+ /* DMA resources should come here */
+ unsigned long dma;
+ u8 *baseaddress; /*base address for i/o ops */
+ u8 *dmabase;
+ isp1763_id *id;
+} isp1763_dev_t;
+
+
+typedef struct isp1763_driver {
+ char *name;
+ unsigned long index; /* HC or DC or OTG */
+ isp1763_id *id; /*device ids */
+ int (*probe) (struct isp1763_dev * dev, isp1763_id * id); /* New device inserted */
+ void (*remove) (struct isp1763_dev * dev); /* Device removed (NULL if not a hot-plug capable driver) */
+
+ void (*suspend) (struct isp1763_dev * dev); /* Device suspended */
+ void (*resume) (struct isp1763_dev * dev); /* Device woken up */
+ void (*remotewakeup) (struct isp1763_dev *dev); /* Remote Wakeup */
+ void (*powerup) (struct isp1763_dev *dev); /* Device poweup mode */
+ void (*powerdown) (struct isp1763_dev *dev); /* Device power down mode */
+} isp_1763_driver_t;
+
+struct usb_device *phci_register_otg_device(struct isp1763_dev *dev);
+
+/*otg exported function from host*/
+int phci_suspend_otg_port(struct isp1763_dev *dev, u32 command);
+int phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command);
+
+extern int isp1763_register_driver(struct isp1763_driver *drv);
+extern void isp1763_unregister_driver(struct isp1763_driver *drv);
+extern int isp1763_request_irq(void (*handler)(struct isp1763_dev * dev, void *isr_data),
+ struct isp1763_dev *dev, void *isr_data);
+extern void isp1763_free_irq(struct isp1763_dev *dev, void *isr_data);
+
+extern u32 isp1763_reg_read32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern u16 isp1763_reg_read16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern u8 isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern void isp1763_reg_write32(isp1763_dev_t * dev, u16 reg, u32 data);
+extern void isp1763_reg_write16(isp1763_dev_t * dev, u16 reg, u16 data);
+extern void isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data);
+extern int isp1763_mem_read(isp1763_dev_t * dev, u32 start_add,
+ u32 end_add, u32 * buffer, u32 length, u16 dir);
+extern int isp1763_mem_write(isp1763_dev_t * dev, u32 start_add,
+ u32 end_add, u32 * buffer, u32 length, u16 dir);
+
+#endif /* __HAL_INTF_H__ */
diff --git a/drivers/usb/host/isp1763-hal.c b/drivers/usb/host/isp1763-hal.c new file mode 100644 index 00000000000..388bf5a0072 --- /dev/null +++ b/drivers/usb/host/isp1763-hal.c @@ -0,0 +1,1757 @@ +/*
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010
+*
+* ISP1763 Linux OTG Controller driver : hal
+*
+* 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.
+*
+* This is the main hardware abstraction layer file. Hardware initialization, interupt
+* processing and read/write routines are handled here.
+*
+* 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/usb.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+#include <linux/version.h>
+/*--------------------------------------------------------------*
+ * linux system include files
+ *--------------------------------------------------------------*/
+#include "isp1763-hal.h"
+#include "isp1763-hal-interface.h"
+#include "isp1763.h"
+
+/*--------------------------------------------------------------*
+ * Local variable Definitions
+ *--------------------------------------------------------------*/
+#ifdef ENABLE_PLX_DMA
+u32 plx9054_reg_read(u32 reg);
+void plx9054_reg_write(u32 reg, u32 data);
+
+u8 *g_pDMA_Write_Buf = 0;
+u8 *g_pDMA_Read_Buf = 0;
+
+#endif
+
+struct isp1763_dev isp1763_loc_dev[ISP1763_LAST_DEV];
+static u32 pci_io_base = 0;
+void *iobase = 0;
+int iolength = 0;
+
+#ifdef NON_PCI
+#else
+static u32 pci_mem_phy0 = 0;
+static u32 pci_mem_len = 0x20000;
+static int isp1763_pci_latency;
+#endif
+
+/*--------------------------------------------------------------*
+ * Local # Definitions
+ *--------------------------------------------------------------*/
+#define PCI_ACCESS_RETRY_COUNT 20
+
+#ifdef NON_PCI
+#define isp1763_driver_name "1763-plat"
+#else
+#define ISP1763_DRIVER_NAME "1763-pci"
+#endif
+
+/*--------------------------------------------------------------*
+ * Local Function
+ *--------------------------------------------------------------*/
+#ifdef NON_PCI
+static void __devexit isp1763_remove (struct device *dev);
+static int __devinit isp1763_probe (struct device *dev);
+//static irqreturn_t isp1763_non_pci_isr (int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t isp1763_non_pci_isr (int irq, void *dev_id);
+#else /*PCI*/
+static void __devexit isp1763_pci_remove(struct pci_dev *dev);
+static int __devinit isp1763_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id);
+
+static irqreturn_t isp1763_pci_isr(int irq, void *dev_id);
+#endif
+
+
+
+/*--------------------------------------------------------------*
+ * ISP1763 Interrupt Service Routine
+ *--------------------------------------------------------------*/
+/*Interrupt Service Routine for device controller*/
+irqreturn_t
+isp1763_pci_dc_isr(int irq, void *data)
+{
+ struct isp1763_dev *dev;
+ u32 int_enable=0;
+ dev = &isp1763_loc_dev[ISP1763_DC];
+
+ hal_entry("%s: Entered\n", __FUNCTION__);
+ /*not ready yet */
+ if (dev->active == 0) {
+ printk("isp1763_pci_dc_isr: dev->active is NULL \n");
+ return IRQ_NONE;
+ }
+
+ /*unblock the device interrupt */
+ isp1763_reg_write16(dev, DEV_UNLOCK_REGISTER, 0xaa37);
+ dev->int_reg =
+ isp1763_reg_read32(dev, DEV_INTERRUPT_REGISTER, dev->int_reg);
+ int_enable = isp1763_reg_read32(dev, INT_ENABLE_REGISTER, int_enable);
+ hal_int("isp1763_pci_dc_isr:INTERRUPT_REGISTER 0x%x\n", dev->int_reg);
+
+ /*clear the interrupt source */
+ isp1763_reg_write32(dev, DEV_INTERRUPT_REGISTER, dev->int_reg);
+ if (dev->int_reg) {
+ if (dev->handler) {
+ dev->handler(dev, dev->isr_data);
+ }
+ }
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ return IRQ_HANDLED;
+}
+
+/* Interrupt Service Routine of isp1763
+ * Reads the source of interrupt and calls the corresponding driver's ISR.
+ * Before calling the driver's ISR clears the source of interrupt.
+ * The drivers can get the source of interrupt from the dev->int_reg field
+ */
+#ifdef NON_PCI
+// irqreturn_t isp1763_non_pci_isr(int irq, void *__data, struct pt_regs *r)
+irqreturn_t isp1763_non_pci_isr(int irq, void *__data)
+#else /*PCI*/
+irqreturn_t
+isp1763_pci_isr(int irq, void *__data)
+#endif
+{
+ u32 irq_mask = 0;
+ struct isp1763_dev *dev;
+ u32 otg_int;
+ hal_entry("%s: Entered\n", __FUNCTION__);
+ /* Process the Host Controller Driver */
+ dev = &isp1763_loc_dev[ISP1763_HC];
+ /* Get the source of interrupts for Host Controller */
+ dev->int_reg = 0;
+ dev->int_reg = isp1763_reg_read16(dev, HC_INTERRUPT_REG, dev->int_reg);
+
+ isp1763_reg_write16(dev, HC_INTERRUPT_REG, dev->int_reg);
+ irq_mask = isp1763_reg_read16(dev, HC_INTENABLE_REG, irq_mask);
+ dev->int_reg &= irq_mask;
+
+ /*process otg interrupt if there is any */
+ if (dev->int_reg & HC_OTG_INTERRUPT) {
+ otg_int = (dev->int_reg & HC_OTG_INTERRUPT);
+ /* Process OTG controller Driver
+ * Since OTG is part of HC interrupt register,
+ * the interrupt source will be HC interrupt Register
+ * */
+ dev = &isp1763_loc_dev[ISP1763_OTG];
+ /* Read the source of OTG_INT and clear the
+ interrupt source */
+ if (dev->handler) {
+ dev->int_reg = otg_int;
+ dev->handler(dev, dev->isr_data);
+ }
+ }
+ dev = &isp1763_loc_dev[ISP1763_HC];
+ if (dev->int_reg & ~HC_OTG_INTERRUPT) {
+ if (dev->handler) {
+ dev->handler(dev, dev->isr_data);
+ }
+ }
+
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ return IRQ_HANDLED;
+} /* End of isp1763_pci_isr */
+
+#ifdef NON_PCI
+/*--------------------------------------------------------------*
+ i* NON_PCI Driver Interface Functions
+ *--------------------------------------------------------------*/
+
+#define ISP1763_ID -1
+
+/* Important fields to initialize for Non-PCI based driver*/
+
+/* The base physical memory address assigned for the ISP176x */
+#define ISP176x_MEM_BASE 0x54000000 //base address
+
+/* The memory range assigned for the ISP176x */
+#define ISP176x_MEM_RANGE 0x10000
+
+/* The IRQ number assigned to the ISP176x */
+#define ISP176x_IRQ_NUM 191
+
+static struct resource isp1763_resources[] = {
+ [0] = {
+ .start= ISP176x_MEM_BASE,
+ .end= (ISP176x_MEM_BASE | ISP176x_MEM_RANGE),
+ .flags= IORESOURCE_MEM,
+ },
+ [1] = {
+ .start= ISP176x_IRQ_NUM,
+ .end= ISP176x_IRQ_NUM,
+ .flags= IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static void isp1763_device_release(struct device *dev)
+{
+ /* Keep this function empty. */
+}
+
+static struct platform_device isp1763_device = {
+ .name = "isp176x_hal",
+ .id = ISP1763_ID,
+ .dev = {
+ .release = isp1763_device_release,
+ },
+
+ .num_resources = ARRAY_SIZE(isp1763_resources),
+ .resource = isp1763_resources,
+};
+
+static struct device_driver isp1763_driver = {
+ .name = "isp176x_hal",
+ .bus = &platform_bus_type,
+ .probe = isp1763_probe,
+ .remove = isp1763_remove,
+#ifndef NON_PCI
+ .suspend = isp1763a_pci_suspend,
+ .resume = isp1763a_pci_resume,
+#endif
+ };
+
+#else /*PCI*/
+
+/*--------------------------------------------------------------*
+ * PCI Driver Interface Functions
+ *--------------------------------------------------------------*/
+
+static const struct pci_device_id __devinitdata isp1763_pci_ids[] = {
+ {
+ /* handle PCI BRIDE manufactured by PLX */
+ class:((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)),
+ class_mask:~0,
+ /* no matter who makes it */
+ vendor: PCI_ANY_ID,
+ device: PCI_ANY_ID,
+ subvendor: PCI_ANY_ID,
+ subdevice: PCI_ANY_ID,
+ },
+ { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, isp1763_pci_ids);
+
+/* Pci driver interface functions */
+static struct pci_driver isp1763_pci_driver = {
+ name:"isp1763-hal",
+ id_table:&isp1763_pci_ids[0],
+ probe:isp1763_pci_probe,
+ remove:isp1763_pci_remove,
+};
+
+#endif
+
+
+/*--------------------------------------------------------------*
+ * ISP1763 Read write routine
+ *--------------------------------------------------------------*/
+
+/* Write a 32 bit Register of isp1763 */
+void
+isp1763_reg_write32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+ /* Write the 32bit to the register address given to us */
+
+#ifdef DATABUS_WIDTH_16
+ writew((u16) data, dev->baseaddress + ((reg) << 1 ));
+ writew((u16) (data >> 16), dev->baseaddress + (((reg + 2) << 1)));
+#else
+ writeb((u8) data, dev->baseaddress + (reg));
+ writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+ writeb((u8) (data >> 16), dev->baseaddress + ((reg + 2)));
+ writeb((u8) (data >> 24), dev->baseaddress + ((reg + 3)));
+#endif
+}
+EXPORT_SYMBOL(isp1763_reg_write32);
+
+/* Read a 32 bit Register of isp1763 */
+u32
+isp1763_reg_read32(struct isp1763_dev *dev, u16 reg, u32 data)
+{
+#ifdef DATABUS_WIDTH_16
+ u16 wvalue1, wvalue2;
+#else
+ u8 bval1, bval2, bval3, bval4;
+#endif
+ data = 0;
+
+#ifdef DATABUS_WIDTH_16
+ wvalue1 = readw(dev->baseaddress + ((reg << 1))) & 0xFFFF ;
+ wvalue2 = readw(dev->baseaddress + (((reg + 2) << 1))) & 0xFFFF ;
+ data |= wvalue2;
+ data <<= 16;
+ data |= wvalue1;
+#else
+ bval1 = readb(dev->baseaddress + (reg));
+ bval2 = readb(dev->baseaddress + (reg + 1));
+ bval3 = readb(dev->baseaddress + (reg + 2));
+ bval4 = readb(dev->baseaddress + (reg + 3));
+ data = 0;
+ data |= bval4;
+ data <<= 8;
+ data |= bval3;
+ data <<= 8;
+ data |= bval2;
+ data <<= 8;
+ data |= bval1;
+
+#endif
+
+ return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read32);
+
+
+/* Read a 16 bit Register of isp1763 */
+u16
+isp1763_reg_read16(struct isp1763_dev * dev, u16 reg, u16 data)
+{
+#ifdef DATABUS_WIDTH_16
+#else
+ u8 bval1, bval2;
+#endif
+
+#ifdef DATABUS_WIDTH_16
+// data = readw(dev->baseaddress + ((reg)));
+ data = (readw(dev->baseaddress + (reg << 1 )));
+#else
+ bval1 = readb(dev->baseaddress + (reg));
+ if (reg == HC_DATA_REG)
+ bval2 = readb(dev->baseaddress + (reg));
+ else
+ bval2 = readb(dev->baseaddress + ((reg + 1)));
+ data = 0;
+ data |= bval2;
+ data <<= 8;
+ data |= bval1;
+
+#endif
+ return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read16);
+
+/* Write a 16 bit Register of isp1763 */
+void
+isp1763_reg_write16(struct isp1763_dev *dev, u16 reg, u16 data)
+{
+#ifdef DATABUS_WIDTH_16
+// writew(data, dev->baseaddress + ((reg)));
+ writew(data, dev->baseaddress + ((reg << 1)));
+#else
+ writeb((u8) data, dev->baseaddress + (reg));
+ if (reg == HC_DATA_REG)
+ writeb((u8) (data >> 8), dev->baseaddress + (reg));
+ else
+ writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
+#endif
+}
+EXPORT_SYMBOL(isp1763_reg_write16);
+
+/* Read a 8 bit Register of isp1763 */
+u8
+isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+ data = readb((dev->baseaddress + (reg << 1)));
+ return data;
+}
+EXPORT_SYMBOL(isp1763_reg_read8);
+
+/* Write a 8 bit Register of isp1763 */
+void
+isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data)
+{
+ writeb(data, (dev->baseaddress + (reg << 1)));
+}
+EXPORT_SYMBOL(isp1763_reg_write8);
+/* Access PLX9054 Register */
+void
+plx9054_reg_write(u32 reg, u32 data)
+{
+ writel(data, iobase + (reg));
+}
+EXPORT_SYMBOL(plx9054_reg_write);
+
+
+void
+plx9054_reg_writeb(u32 reg, u32 data)
+{
+ writeb(data, iobase + (reg));
+}
+EXPORT_SYMBOL(plx9054_reg_writeb);
+
+u32
+plx9054_reg_read(u32 reg)
+{
+ u32 uData;
+
+ uData = readl(iobase + (reg));
+
+ return uData;
+}
+EXPORT_SYMBOL(plx9054_reg_read);
+
+u8
+plx9054_reg_readb(u32 reg)
+{
+ u8 bData;
+
+ bData = readb(iobase + (reg));
+
+ return bData;
+}
+EXPORT_SYMBOL(plx9054_reg_readb);
+
+#ifdef ENABLE_PLX_DMA
+
+int
+isp1763_mem_read_dma(struct isp1763_dev *dev, u32 start_add,
+ u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+ u8 *pDMABuffer = 0;
+ u32 uPhyAddress = 0;
+ u32 ulDmaCmdStatus, fDone = 0;
+
+
+ /* Set start memory location for write*/
+ isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+ udelay(1);
+
+ /* Malloc DMA safe buffer and convert to PHY Address*/
+ pDMABuffer = g_pDMA_Read_Buf;
+
+ if (pDMABuffer == NULL) {
+ printk("Cannnot allocate DMA safe memory for DMA read\n");
+ return -1;
+ }
+ uPhyAddress = virt_to_phys(pDMABuffer);
+
+ /* Program DMA transfer*/
+
+ /*DMA CHANNEL 1 PCI ADDRESS */
+ plx9054_reg_write(0x98, uPhyAddress);
+
+ /*DMA CHANNEL 1 LOCAL ADDRESS */
+ plx9054_reg_write(0x9C, 0x40);
+
+ /*DMA CHANNEL 1 TRANSFER SIZE */
+ plx9054_reg_write(0xA0, length);
+
+ /*DMA CHANNEL 1 DESCRIPTOR POINTER */
+ plx9054_reg_write(0xA4, 0x08);
+
+ /*DMA THRESHOLD */
+ plx9054_reg_write(0xB0, 0x77220000);
+
+ /*DMA CHANNEL 1 COMMAND STATUS */
+ plx9054_reg_writeb(0xA9, 0x03);
+
+
+ do {
+ ulDmaCmdStatus = plx9054_reg_read(0xA8);
+
+ if ((ulDmaCmdStatus & 0x00001000)) {
+ ulDmaCmdStatus |= 0x00000800;
+ plx9054_reg_write(0xA8, ulDmaCmdStatus);
+ fDone = 1;
+ } else {
+
+ fDone = 0;
+ }
+ } while (fDone == 0);
+
+ /* Copy DMA buffer to upper layer buffer*/
+ memcpy(buffer, pDMABuffer, length);
+
+
+ return 0;
+}
+
+int
+isp1763_mem_write_dma(struct isp1763_dev *dev,
+ u32 start_add, u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+ u8 *pDMABuffer = 0;
+ u8 bDmaCmdStatus = 0;
+ u32 uPhyAddress;
+ u32 ulDmaCmdStatus,fDone = 0;
+
+ isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+ udelay(1);
+
+ /* Malloc DMA safe buffer and convert to PHY Address*/
+ pDMABuffer = g_pDMA_Write_Buf;
+
+ if (pDMABuffer == NULL) {
+ printk("Cannnot allocate DMA safe memory for DMA write\n");
+ return -1;
+ }
+ /* Copy content to DMA safe buffer*/
+ memcpy(pDMABuffer, buffer, length);
+ uPhyAddress = virt_to_phys(pDMABuffer);
+
+
+ /*DMA CHANNEL 1 PCI ADDRESS */
+ plx9054_reg_write(0x98, uPhyAddress);
+
+ /*DMA CHANNEL 1 LOCAL ADDRESS */
+ plx9054_reg_write(0x9C, 0x40);
+
+ /*DMA CHANNEL 1 TRANSFER SIZE */
+ plx9054_reg_write(0xA0, length);
+
+ /*DMA CHANNEL 1 DESCRIPTOR POINTER */
+ plx9054_reg_write(0xA4, 0x00);
+
+ /*DMA THRESHOLD */
+ plx9054_reg_write(0xB0, 0x77220000);
+
+ /*DMA CHANNEL 1 COMMAND STATUS */
+ bDmaCmdStatus = plx9054_reg_readb(0xA9);
+ bDmaCmdStatus |= 0x03;
+ plx9054_reg_writeb(0xA9, bDmaCmdStatus);
+
+
+ do {
+ ulDmaCmdStatus = plx9054_reg_read(0xA8);
+
+ if ((ulDmaCmdStatus & 0x00001000)){
+ ulDmaCmdStatus |= 0x00000800;
+ plx9054_reg_write(0xA8, ulDmaCmdStatus);
+ fDone = 1;
+ } else {
+ fDone = 0;
+ }
+ } while (fDone == 0);
+
+ return 0;
+}
+
+#endif
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_mem_read
+ *
+ * Memory read using PIO method.
+ *
+ * Input: struct isp1763_driver *drv --> Driver structure.
+ * u32 start_add --> Starting address of memory
+ * u32 end_add ---> End address
+ *
+ * u32 * buffer --> Buffer pointer.
+ * u32 length ---> Length
+ * u16 dir ---> Direction ( Inc or Dec)
+ *
+ * Output int Length ----> Number of bytes read
+ *
+ * Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+/* Memory read function PIO */
+
+int
+isp1763_mem_read(struct isp1763_dev *dev, u32 start_add,
+ u32 end_add, u32 * buffer, u32 length, u16 dir)
+{
+ u8 *temp_base_mem = 0;
+#ifdef ENABLE_PLX_DMA
+#else
+ u8 *one = (u8 *) buffer;
+ u16 *two = (u16 *) buffer;
+ u32 w;
+ u32 w2;
+// u8 bvalue;
+// u16 wvalue;
+#endif
+ u32 a = (u32) length;
+
+
+#ifdef NON_PCI // not sure why
+ temp_base_mem= (u8 *)(dev->baseaddress + start_add);
+#else /*PCI*/
+ temp_base_mem = (dev->baseaddress + (start_add));
+#endif
+
+ if (buffer == 0) {
+ printk("Buffer address zero\n");
+ return 0;
+ }
+#ifdef ENABLE_PLX_DMA
+
+ isp1763_mem_read_dma(dev, start_add, end_add, buffer, length, dir);
+ a = 0;
+
+#else
+ isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+
+ last:
+ w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+ w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+ w2 <<= 16;
+ w = w | w2;
+ if (a == 1) {
+ *one = (u8) w;
+ return 0;
+ }
+ if (a == 2) {
+ *two = (u16) w;
+ return 0;
+ }
+
+ if (a == 3) {
+ *two = (u16) w;
+ two += 1;
+ w >>= 16;
+ *two = (u8) (w);
+ return 0;
+
+ }
+ while (a > 0) {
+ *buffer = w;
+ a -= 4;
+ if (a <= 0)
+ break;
+ if (a < 4) {
+ buffer += 1;
+ one = (u8 *) buffer;
+ two = (u16 *) buffer;
+ goto last;
+ }
+ buffer += 1;
+ w = isp1763_reg_read16(dev, HC_DATA_REG, w);
+ w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
+ w2 <<= 16;
+ w = w | w2;
+ }
+#endif
+ return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_read);
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_mem_write
+ *
+ * Memory write using PIO method.
+ *
+ * Input: struct isp1763_driver *drv --> Driver structure.
+ * u32 start_add --> Starting address of memory
+ * u32 end_add ---> End address
+ *
+ * u32 * buffer --> Buffer pointer.
+ * u32 length ---> Length
+ * u16 dir ---> Direction ( Inc or Dec)
+ *
+ * Output int Length ----> Number of bytes read
+ *
+ * Called by: system function
+ *
+ *
+ *--------------------------------------------------------------*/
+
+/* Memory read function IO */
+int
+isp1763_mem_write(struct isp1763_dev *dev,
+ u32 start_add, u32 end_add,
+ u32 * buffer, u32 length, u16 dir)
+{
+#ifdef ENABLE_PLX_DMA
+#else
+ u8 *temp_base_mem = 0;
+// u8 *temp = (u8 *) buffer;
+ u8 one = (u8) (*buffer);
+ u16 two = (u16) (*buffer);
+#endif
+ int a = length;
+
+#ifdef ENABLE_PLX_DMA
+
+ isp1763_mem_write_dma(dev, start_add, end_add, buffer, length, dir);
+ a = 0;
+
+#else
+ isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
+
+#ifdef NON_PCI
+ temp_base_mem= (u8 *)(dev->baseaddress + start_add);
+#else /*PCI*/
+ temp_base_mem = (dev->baseaddress + (start_add));
+#endif
+
+ if (a == 1) {
+ isp1763_reg_write16(dev, HC_DATA_REG, one);
+ return 0;
+ }
+ if (a == 2) {
+ isp1763_reg_write16(dev, HC_DATA_REG, two);
+ return 0;
+ }
+
+ while (a > 0) {
+ isp1763_reg_write16(dev, HC_DATA_REG, (u16) (*buffer));
+ if (a >= 3)
+ isp1763_reg_write16(dev, HC_DATA_REG,
+ (u16) ((*buffer) >> 16));
+ temp_base_mem = temp_base_mem + 4;
+ start_add += 4;
+ a -= 4;
+ if (a <= 0)
+ break;
+ buffer += 1;
+
+ }
+#endif
+ return ((a < 0) || (a == 0)) ? 0 : (-1);
+
+}
+EXPORT_SYMBOL(isp1763_mem_write);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_request_irq
+ *
+ * This function registers the ISR of driver with this driver.
+ * Since there is only one interrupt line, when the first driver
+ * is registerd, will call the system function request_irq. The PLX
+ * bridge needs enabling of interrupt in the interrupt control register to
+ * pass the local interrupts to the PCI (cpu).
+ * For later registrations will just update the variables. On ISR, this driver
+ * will look for registered handlers and calls the corresponding driver's
+ * ISR "handler" function with "isr_data" as parameter.
+ *
+ * Input: struct
+ * (void (*handler)(struct isp1763_dev *, void *)-->handler.
+ * isp1763_driver *drv --> Driver structure.
+ * Output result
+ * 0= complete
+ * 1= error.
+ *
+ * Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+int
+isp1763_request_irq(void (*handler) (struct isp1763_dev *, void *),
+ struct isp1763_dev *dev, void *isr_data)
+{
+ int result = 0;
+#ifndef NON_PCI
+ u32 intcsr = 0;
+#endif
+
+ hal_entry("%s: Entered\n", __FUNCTION__);
+ hal_int("isp1763_request_irq: dev->index %x\n", dev->index);
+
+#ifdef NON_PCI
+
+ result= request_irq(dev->irq, isp1763_non_pci_isr,IRQF_SHARED, dev->name, isr_data);
+ hal_int(KERN_NOTICE "isp1763_request_irq result: %x for IRQ %i\n",result, dev->irq);
+#else /* PCI MODE */
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ if (dev->index == ISP1763_DC) {
+ result = request_irq(dev->irq, isp1763_pci_dc_isr,
+ SA_SHIRQ, dev->name, isr_data);
+ } else if (dev->index == ISP1763_HC) {
+ result = request_irq(dev->irq, isp1763_pci_isr,
+ SA_SHIRQ, dev->name, isr_data);
+ }
+#else
+ if (dev->index == ISP1763_DC) {
+ result = request_irq(dev->irq,
+ isp1763_pci_dc_isr,
+ IRQF_SHARED,
+ dev->name,
+ isr_data);
+ } else if (dev->index == ISP1763_HC) {
+ result = request_irq(dev->irq,
+ isp1763_pci_isr,
+ IRQF_SHARED,
+ dev->name,
+ isr_data);
+ }
+#endif
+
+#endif
+
+
+
+#ifdef NON_PCI
+#else /*PCI*/
+ /*CONFIGURE PCI/PLX interrupt */
+ intcsr = readl(iobase + 0x68);
+ intcsr |= 0x900;
+ writel(intcsr, iobase + 0x68);
+#endif
+
+
+
+ udelay(30);
+ /*Interrupt handler routine */
+ dev->handler = handler;
+ dev->isr_data = isr_data;
+ hal_int("isp1763_request_irq: dev->handler %s\n", dev->handler);
+ hal_int("isp1763_request_irq: dev->isr_data %x\n", dev->isr_data);
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ return result;
+} /* End of isp1763_request_irq */
+EXPORT_SYMBOL(isp1763_request_irq);
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_free_irq
+ *
+ * This function de-registers the ISR of driver with this driver.
+ * Since there is only one interrupt line, when the last driver
+ * is de-registerd, will call the system function free_irq. The PLX
+ * bridge needs disabling of interrupt in the interrupt control register to
+ * block the local interrupts to the PCI (cpu).
+ *
+ * Input: struct
+ * (void (*handler)(struct isp1763_dev *, void *)-->handler.
+ * isp1763_driver *drv --> Driver structure.
+ * Output result
+ * 0= complete
+ * 1= error.
+ *
+ * Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+void
+isp1763_free_irq(struct isp1763_dev *dev, void *isr_data)
+{
+
+
+#ifdef NON_PCI
+ free_irq(dev->irq,isr_data);
+#else /*PCI*/
+ u32 intcsr;
+ hal_int(("isp1763_free_irq(dev=%p,isr_data=%p)\n", dev, isr_data));
+ free_irq(dev->irq, isr_data);
+ /*disable the plx/pci interrupt */
+ intcsr = readl(iobase + 0x68);
+ intcsr &= ~0x900;
+ writel(intcsr, iobase + 0x68);
+#endif
+
+
+} /* isp1763_free_irq */
+EXPORT_SYMBOL(isp1763_free_irq);
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_register_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will call the probe function of the driver if the ISP1763
+ * corresponding to the driver is enabled
+ *
+ * Input: struct isp1763_driver *drv --> Driver structure.
+ * Output result
+ * 0= complete
+ * 1= error.
+ *
+ * Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+int
+isp1763_register_driver(struct isp1763_driver *drv)
+{
+ struct isp1763_dev *dev;
+ int result;
+ isp1763_id *id;
+
+ hal_entry("%s: Entered\n", __FUNCTION__);
+ info("isp1763_register_driver(drv=%p) \n", drv);
+
+ if (!drv)
+ return -EINVAL;
+ dev = &isp1763_loc_dev[drv->index];
+ if (drv->index == ISP1763_DC) {
+ dev->id = drv->id;
+ result = drv->probe(dev, drv->id);
+ } else {
+ id = drv->id;
+ dev->id = drv->id;
+ if (dev->active)
+ result = drv->probe(dev, id);
+ else
+ result = -ENODEV;
+ }
+
+ if (result >= 0) {
+ dev->driver = drv;
+ }
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ return result;
+} /* End of isp1763_register_driver */
+EXPORT_SYMBOL(isp1763_register_driver);
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_unregister_driver
+ *
+ * This function is used by top driver (OTG, HCD, DCD) to de-register
+ * their communication functions (probe, remove, suspend, resume) using
+ * the drv data structure.
+ * This function will check whether the driver is registered or not and
+ * call the remove function of the driver if registered
+ *
+ * Input: struct isp1763_driver *drv --> Driver structure.
+ * Output result
+ * 0= complete
+ * 1= error.
+ *
+ * Called by: system function module_init
+ *
+ *
+ *--------------------------------------------------------------*/
+
+
+void
+isp1763_unregister_driver(struct isp1763_driver *drv)
+{
+ struct isp1763_dev *dev;
+ hal_entry("%s: Entered\n", __FUNCTION__);
+
+ info("isp1763_unregister_driver(drv=%p)\n", drv);
+ dev = &isp1763_loc_dev[drv->index];
+ if (dev->driver == drv) {
+ /* driver registered is same as the requestig driver */
+ drv->remove(dev);
+ dev->driver = NULL;
+ info(": De-registered Driver %s\n", drv->name);
+ return;
+ }
+ hal_entry("%s: Exit\n", __FUNCTION__);
+} /* End of isp1763_unregister_driver */
+EXPORT_SYMBOL(isp1763_unregister_driver);
+
+/*--------------------------------------------------------------*
+ * ISP1763 PCI driver interface routine.
+ *--------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_pci_module_init
+ *
+ * This is the module initialization function. It registers to
+ * PCI driver for a PLX PCI bridge device. And also resets the
+ * internal data structures before registering to PCI driver.
+ *
+ * Input: void
+ * Output result
+ * 0= complete
+ * 1= error.
+ *
+ * Called by: system function module_init
+ *
+ *
+ *
+ -------------------------------------------------------------------*/
+#ifdef NON_PCI
+static int __init
+ isp1763_module_init (void)
+#else /*PCI*/
+static int __init
+isp1763_pci_module_init(void)
+#endif
+{
+ int result = 0;
+ hal_entry("%s: Entered\n", __FUNCTION__);
+#ifdef NON_PCI
+ hal_entry(KERN_NOTICE "+isp1763_module_init \n");
+#else
+ hal_entry(KERN_NOTICE "+isp1763_pci_module_init \n");
+#endif
+ memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+
+#ifdef NON_PCI
+if((result = platform_device_register(&isp1763_device)) == 0) {
+
+ hal_init(KERN_NOTICE "platform_device_register() success: result :0x%08x\n", result); // jimmy
+
+ if((result = driver_register(&isp1763_driver)) < 0) {
+ platform_device_unregister(&isp1763_device);
+ hal_init(KERN_NOTICE "driver_register() fail: result :0x%08x\n", result); // jimmy
+ return result;
+ } else {
+ hal_init(KERN_NOTICE "driver_register() success: result :0x%08x\n", result); // jimmy
+ }
+} else { // platform_device_register failed!
+ hal_init(KERN_NOTICE "platform_device_register() fail: result :0x%08x\n", result); // jimmy
+ return result;
+}
+
+ hal_init(KERN_NOTICE "-isp1763_module_init \n");
+
+#else /*PCI*/
+ if ((result = pci_register_driver(&isp1763_pci_driver)) < 0) {
+ hal_init("PCI Iinitialization Fail(error = %d)\n", result);
+ return result;
+ } else
+ hal_init(": %s PCI Initialization Success \n", ISP1763_DRIVER_NAME);
+#endif
+
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ return result;
+}
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_pci_module_cleanup
+ *
+ * This is the module cleanup function. It de-registers from
+ * PCI driver and resets the internal data structures.
+ *
+ * Input: void
+ * Output void
+ *
+ * Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+
+#ifdef NON_PCI
+static void __exit isp1763_module_cleanup (void)
+{
+ hal_init("Hal Module Cleanup\n");
+ driver_unregister(&isp1763_driver);
+ platform_device_unregister(&isp1763_device);
+ memset(isp1763_loc_dev,0,sizeof(isp1763_loc_dev));
+}
+#else /*PCI*/
+static void __exit
+isp1763_pci_module_cleanup(void)
+{
+ hal_init("Hal Module Cleanup\n");
+ pci_unregister_driver(&isp1763_pci_driver);
+ memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
+}
+#endif
+
+
+#ifdef MEMORY_TEST
+static void
+Ph1763MemoryTest(struct isp1763_dev *loc_dev, u32 ulMemBaseAddr, u32 ulMemLen)
+{
+ u32 dwNumDwords;
+ u16 wValue;
+ u16 wlRegData;
+ u16 wTestCnt;
+ u16 wlTestData;
+
+ dwNumDwords = 0;
+ wlRegData = 0;
+
+ if (ulMemLen % 4) {
+ dwNumDwords = (ulMemLen / 4) + 1;
+ } else {
+ dwNumDwords = (ulMemLen / 4);
+ }
+
+ hal_init(KERN_NOTICE "Base Addr %x and MemLen %x\n", ulMemBaseAddr,
+ ulMemLen);
+
+ isp1763_reg_write16(loc_dev, HC_MEM_READ_REG, ulMemBaseAddr);
+
+ wTestCnt = 0;
+ wlTestData = 0x0;
+ for (wlRegData = 0; wlRegData < dwNumDwords * 2; wlRegData++) {
+ isp1763_reg_write16(loc_dev, HC_DATA_REG, wlTestData);
+ wTestCnt += 2;
+ wlTestData++;
+ }
+
+ isp1763_reg_write16(loc_dev, HC_MEM_READ_REG, ulMemBaseAddr);
+ wTestCnt = 0;
+ wlTestData = 0;
+ for (wlRegData = 0; wlRegData < dwNumDwords * 2; wlRegData++) {
+ wValue = isp1763_reg_read16(loc_dev, HC_DATA_REG, wValue);
+ if (wlTestData != wValue) {
+ if (ulMemBaseAddr == 0xC00) {
+ printk(KERN_NOTICE
+ "ATLTD Init Error at address 0x%x:Expected value is0x%x:Current Value %x\n",
+ (ulMemBaseAddr + wTestCnt), wlTestData,
+ wValue);
+ } else if (ulMemBaseAddr == 0x400) {
+ printk(KERN_NOTICE
+ "INTLTD Init Error at address 0x%x:Expected value is0x%x:Current Value %x\n",
+ (ulMemBaseAddr + wTestCnt), wlTestData,
+ wValue);
+ } else if (ulMemBaseAddr == 0x800) {
+ printk(KERN_NOTICE
+ "ISOTD Init Error at address 0x%x:Expected value is0x%x:Current Value %x\n",
+ (ulMemBaseAddr + wTestCnt), wlTestData,
+ wValue);
+ } else {
+ printk(KERN_NOTICE
+ "Payload Init Error at address 0x%x:Expected value is0x%x:Current Value %x\n",
+ (ulMemBaseAddr + wTestCnt), wlTestData,
+ wValue);
+ }
+
+ }
+ wTestCnt += 2;
+ wlTestData++;
+ }
+
+
+}
+#endif
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_pci_probe
+ *
+ * PCI probe function of ISP1763
+ * This function is called from PCI Driver as an initialization function
+ * when it founds the PCI device. This functions initializes the information
+ * for the 3 Controllers with the assigned resources and tests the register
+ * access to these controllers and do a software reset and makes them ready
+ * for the drivers to play with them.
+ *
+ * Input:
+ * struct pci_dev *dev ----> PCI Devie data structure
+ * const struct pci_device_id *id ----> PCI Device ID
+ * Output void
+ *
+ * Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------**/
+#ifdef NON_PCI
+static int __devinit isp1763_probe (struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct isp1763_dev *loc_dev;
+ void *address = 0;
+ int status = 1;
+ unsigned long word32 = 0;
+#ifndef NON_PCI
+ unsigned long base_addr;
+#endif
+ unsigned long temp, i;
+
+ /* Get the Host Controller IO and INT resources
+ */
+ loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+
+ if (loc_dev != NULL)
+ hal_init("loc_dev: 0x%08x\n", loc_dev);
+ else
+ hal_init("loc_dev is NULL");
+
+ if (pdev != NULL)
+ {
+ hal_init("pdev->resource[0].start 0x%x\n", pdev->resource[0].start);
+ hal_init("pdev->resource[0].end 0x%x\n", pdev->resource[0].end);
+ hal_init("Range: 0x%x\n", (pdev->resource[0].end - pdev->resource[0].start -1));
+ }
+ else
+ hal_init("pdev is NULL");
+
+ loc_dev->irq = platform_get_irq(pdev, 0);
+ if (loc_dev != NULL)
+ hal_init("loc_dev->irq: 0x%x\n", loc_dev->irq);
+ else
+ hal_init("loc_dev is NULL");
+
+ loc_dev->io_base = pdev->resource[0].start;
+ loc_dev->start = pdev->resource[0].start;
+ loc_dev->length = (pdev->resource[0].end - pdev->resource[0].start -1);
+ loc_dev->io_len = (pdev->resource[0].end - pdev->resource[0].start -1); /*64K*/
+ loc_dev->index = ISP1763_HC;/*zero*/
+
+ loc_dev->io_len = ISP176x_MEM_RANGE;
+ if(check_mem_region(loc_dev->io_base, loc_dev->length) < 0) {
+ hal_init("host controller already in use\n");
+ return -EBUSY;
+ }
+ if(!request_mem_region(loc_dev->io_base, loc_dev->length, isp1763_driver_name)){
+ hal_init("host controller already in use\n");
+ return -EBUSY;
+ }
+
+ /*map available memory*/
+// address = IO_ADDRESS(loc_dev->io_base); // jimmy
+ address = ioremap(loc_dev->start, loc_dev->length);
+
+ if(address == NULL){
+ err("memory map problem\n");
+ release_mem_region(loc_dev->io_base, loc_dev->length);
+ return -ENOMEM;
+ }
+ if (loc_dev != NULL)
+ hal_init("Base: 0x%x with Range: 0x%x remapped to 0x%x\n", loc_dev->io_base, loc_dev->length, address);
+ else
+ hal_init("loc_dev is NULL");
+
+ loc_dev->baseaddress = (u8*)address;
+ loc_dev->dmabase = (u8*) 0;
+
+ if (loc_dev != NULL)
+ hal_init("isp1763 HC MEM Base= %p irq = %d\n",
+ loc_dev->baseaddress,loc_dev->irq);
+ else
+ hal_init("loc_dev is NULL");
+
+ /* Try to check whether we can access Scratch Register of
+ * Host Controller or not. The initial PCI access is retried until
+ * local init for the PCI bridge is completed
+ */
+ loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+
+
+
+ hal_init("Initiating Scratch register test\n");
+
+
+ for(i = 0, word32 = 1; i < 16; i ++)
+ {
+ word32 = (1 << i);
+ isp1763_reg_write16(loc_dev, HC_SCRATCH_REG, (__u16)word32);
+// isp1763_reg_write32(loc_dev, HC_SCRATCH_REG, (__u16)word32);
+ udelay(1);
+
+ temp = 0;
+ temp = isp1763_reg_read32(loc_dev, DC_CHIPID, temp);
+ udelay(1);
+
+ hal_init("Chip ID is 0x%08x\n", temp);
+
+ if (temp != 0x176320) //For ES2
+ {
+ hal_init("Index%d: Chip ID mismatch after writing 0x%04x read=0x%08x\n", i, (__u16)word32, temp);
+ }
+ else
+ hal_init("Index%d: Chip ID is 0x%08x\n", i, temp);
+
+ temp = 0;
+ temp = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, (__u16)temp);
+// temp = isp1763_reg_read32(loc_dev, HC_SCRATCH_REG, (__u16)temp);
+ udelay(1);
+
+ if(temp != word32)
+ {
+ hal_init("ERROR ====> Writing 0x%08x to Scrath Reg failed! read=0x%08x\n", word32, temp);
+ }
+ }
+
+ hal_init("Scratch test completed...have a nice day!\n");
+
+ memcpy(loc_dev->name, isp1763_driver_name, sizeof(isp1763_driver_name));
+ loc_dev->name[sizeof(isp1763_driver_name)] = 0;
+ loc_dev->active = 1;
+
+ loc_dev->dev = pdev;
+
+// hal_data.irq_usage = 0;
+ dev_set_drvdata(dev, loc_dev);
+ hal_init("Exiting HAL initialization....SUCCESS!!!\n");
+
+ hal_entry("%s: Exit\n",__FUNCTION__);
+ return 0;
+
+clean://check why?
+// release_mem_region(loc_dev->io_base, loc_dev->io_len);
+ iounmap(loc_dev->baseaddress);
+ hal_entry("%s: Exit\n",__FUNCTION__);
+ return status;
+} /* End of isp1763_probe */
+
+#else /*PCI*/
+
+static int __devinit
+isp1763_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ u8 latency, limit;
+ u32 reg_data = 0;
+ int retry_count;
+ struct isp1763_dev *loc_dev;
+ void *address = 0;
+ int length = 0;
+ int status = 1;
+ u32 ul_busregion_descr = 0;
+ u32 hwmodectrl = 0, chipid=0;
+ u16 us_fpga_conf=0;
+#ifdef ENABLE_PLX_DMA
+ u32 ulDmaModeCh1;
+#endif
+ u32 temp = 0;
+
+
+ hal_entry("%s: Entered\n", __FUNCTION__);
+
+ hal_init(("isp1763_pci_probe(dev=%p)\n", dev));
+ hal_init(KERN_NOTICE "+isp1763_pci_probe \n");
+ if (pci_enable_device(dev) < 0) {
+ err("failed in enabing the device\n");
+ return -ENODEV;
+ }
+ if (!dev->irq) {
+ err("found ISP1763 device with no IRQ assigned.");
+ err("check BIOS settings!");
+ return -ENODEV;
+ }
+ /* Grab the PLX PCI mem maped port start address we need */
+ pci_io_base = pci_resource_start(dev, 0);
+ hal_init(("isp1763 pci IO Base= %x\n", pci_io_base));;
+
+ iolength = pci_resource_len(dev, 0);
+ hal_init(KERN_NOTICE "isp1763 pci io length %x\n", iolength);
+ hal_init(("isp1763 pci io length %d\n", iolength));
+
+ if (!request_mem_region(pci_io_base, iolength, "ISP1763 IO MEM")) {
+ err("host controller already in use1\n");
+ return -EBUSY;
+ }
+ iobase = ioremap_nocache(pci_io_base, iolength);
+ if (!iobase) {
+ err("can not map io memory to system memory\n");
+ release_mem_region(pci_io_base, iolength);
+ return -ENOMEM;
+ }
+ /* Grab the PLX PCI shared memory of the ISP1763 we need */
+ pci_mem_phy0 = pci_resource_start(dev, 3);
+ hal_init(("isp1763 pci base address = %x\n", pci_mem_phy0));
+
+ /* Get the Host Controller IO and INT resources
+ */
+ loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+ loc_dev->irq = dev->irq;
+ loc_dev->io_base = pci_mem_phy0;
+ loc_dev->start = pci_mem_phy0;
+ loc_dev->length = pci_mem_len;
+ loc_dev->io_len = pci_mem_len; /*64K */
+ loc_dev->index = ISP1763_HC; /*zero */
+
+ length = pci_resource_len(dev, 3);
+ hal_init(KERN_NOTICE "isp1763 pci resource length %x\n", length);
+ if (length < pci_mem_len) {
+ err("memory length for this resource is less than required\n");
+ release_mem_region(pci_io_base, iolength);
+ iounmap(iobase);
+ return -ENOMEM;
+
+ }
+ loc_dev->io_len = length;
+ if (check_mem_region(loc_dev->io_base, length) < 0) {
+ err("host controller already in use\n");
+ release_mem_region(pci_io_base, iolength);
+ iounmap(iobase);
+ return -EBUSY;
+ }
+ if (!request_mem_region(loc_dev->io_base, length, ISP1763_DRIVER_NAME)) {
+ err("host controller already in use\n");
+ release_mem_region(pci_io_base, iolength);
+ iounmap(iobase);
+ return -EBUSY;
+
+ }
+
+ /*map available memory */
+ address = ioremap_nocache(pci_mem_phy0, length);
+ if (address == NULL) {
+ err("memory map problem\n");
+ release_mem_region(pci_io_base, iolength);
+ iounmap(iobase);
+ release_mem_region(loc_dev->io_base, length);
+ return -ENOMEM;
+ }
+
+ loc_dev->baseaddress = (u8 *) address;
+ loc_dev->dmabase = (u8 *) iobase;
+
+ hal_init(("isp1763 HC MEM Base= %p irq = %d\n",
+ loc_dev->baseaddress, loc_dev->irq));
+
+#ifdef DATABUS_WIDTH_16
+
+ ul_busregion_descr = readl(iobase + 0xF8);
+ hal_init(KERN_NOTICE "setting plx bus width to 16:BusRegionDesc %x \n",
+ ul_busregion_descr);
+ ul_busregion_descr &= 0xFFFFFFFC;
+ ul_busregion_descr |= 0x00000001;
+ writel(ul_busregion_descr, iobase + 0xF8);
+ ul_busregion_descr = readl(iobase + 0xF8);
+ hal_init(KERN_NOTICE "BusRegionDesc %x \n", ul_busregion_descr);
+
+#ifdef ENABLE_PLX_DMA
+
+ ulDmaModeCh1 = plx9054_reg_read(0x94);
+
+ /* Set as 16 bit mode */
+ ulDmaModeCh1 &= 0xFFFFFFFC;
+ ulDmaModeCh1 |= 0x00020401;
+
+ ulDmaModeCh1 |= 0x00000800; /*Holds local address bus constant*/
+
+ plx9054_reg_write(0x94, ulDmaModeCh1);
+
+
+#endif /*ENABLE_PLX_DMA*/
+
+#else
+
+ ul_busregion_descr = readl(iobase + 0xF8);
+ hal_init(KERN_NOTICE "setting plx bus width to 8:BusRegionDesc %x \n",
+ ul_busregion_descr);
+ ul_busregion_descr &= 0xFFFFFFFC;
+ writel(ul_busregion_descr, iobase + 0xF8);
+ hal_init(KERN_NOTICE "BusRegionDesc %x \n", ul_busregion_descr);
+
+#endif
+
+
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ hal_init("After setting plx, chip id:%x \n", chipid);
+
+
+#ifdef DATABUS_WIDTH_16
+
+ isp1763_reg_write16(loc_dev, FPGA_CONFIG_REG, 0xBf);
+ us_fpga_conf =
+ isp1763_reg_read16(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 1 %x \n", us_fpga_conf);
+ isp1763_reg_write16(loc_dev, FPGA_CONFIG_REG, 0x3f);
+ us_fpga_conf =
+ isp1763_reg_read16(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 2 %x \n", us_fpga_conf);
+ mdelay(5);
+ isp1763_reg_write16(loc_dev, FPGA_CONFIG_REG, 0xFf);
+ us_fpga_conf =
+ isp1763_reg_read16(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 3 %x \n", us_fpga_conf);
+
+#else
+
+ us_fpga_conf =
+ isp1763_reg_read8(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "INIT FPGA CONF REG 1 %x \n", us_fpga_conf);
+ isp1763_reg_write8(loc_dev, FPGA_CONFIG_REG, 0xB7);
+ us_fpga_conf =
+ isp1763_reg_read8(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 1 %x \n", us_fpga_conf);
+
+ isp1763_reg_write8(loc_dev, FPGA_CONFIG_REG, 0x37);
+ us_fpga_conf =
+ isp1763_reg_read8(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 2 %x \n", us_fpga_conf);
+ mdelay(5);
+
+ isp1763_reg_write8(loc_dev, FPGA_CONFIG_REG, 0xF7);
+ us_fpga_conf =
+ isp1763_reg_read8(loc_dev, FPGA_CONFIG_REG, us_fpga_conf);
+ hal_init(KERN_NOTICE "FPGA CONF REG 3 %x \n", us_fpga_conf);
+ mdelay(1);
+
+#endif
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ hal_init("After setting fpga, chip id:%x \n", chipid);
+
+
+
+
+#ifdef ISP1763_DEVICE
+
+ /*initialize device controller framework */
+ loc_dev = &(isp1763_loc_dev[ISP1763_DC]);
+ loc_dev->irq = dev->irq;
+ loc_dev->io_base = pci_mem_phy0;
+ loc_dev->start = pci_mem_phy0;
+ loc_dev->length = pci_mem_len;
+ loc_dev->io_len = pci_mem_len;
+ loc_dev->index = ISP1763_DC;
+ loc_dev->baseaddress = address;
+ loc_dev->active = 1;
+ memcpy(loc_dev->name, "isp1763_dev", 11);
+ loc_dev->name[12] = '\0';
+ {
+ /*reset the host controller */
+ temp |= 0x1;
+ isp1763_reg_write16(loc_dev, HC_RESET_REG, temp); //0xB8
+ mdelay(20);
+ temp = 0;
+ temp |= 0x2;
+ isp1763_reg_write16(loc_dev, HC_RESET_REG, temp);
+
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ hal_init("After hc reset, chip id:%x \n", chipid);
+ hwmodectrl =
+ isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+ hal_init(KERN_NOTICE "Mode Ctrl Value : %x\n", hwmodectrl);
+#ifdef DATABUS_WIDTH_16
+ hwmodectrl &= 0xFFEF; /*enable the 16 bit bus */
+#else
+ hwmodectrl |= 0x0010; /*enable the 8 bit bus */
+#endif
+ isp1763_reg_write16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+ hwmodectrl =
+ isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
+ hal_init(KERN_NOTICE "Mode Ctrl Value after buswidth: %x\n",
+ hwmodectrl);
+
+ }
+
+ {
+ u32 chipid = 0;
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ hal_init("After hwmode, chip id:%x \n", chipid);
+ info("pid %04x, vid %04x\n", (chipid & 0xffff), (chipid >> 16));
+ }
+ hal_init(("isp1763 DC MEM Base= %lx irq = %d\n",
+ loc_dev->io_base, loc_dev->irq));
+ /* Get the OTG Controller IO and INT resources
+ * OTG controller resources are same as Host Controller resources
+ */
+ loc_dev = &(isp1763_loc_dev[ISP1763_OTG]);
+ loc_dev->irq = dev->irq; /*same irq also */
+ loc_dev->io_base = pci_mem_phy0;
+ loc_dev->start = pci_mem_phy0;
+ loc_dev->length = pci_mem_len;
+ loc_dev->io_len = pci_mem_len;
+ loc_dev->index = ISP1763_OTG;
+ loc_dev->baseaddress = address; /*having the same address as of host */
+ loc_dev->active = 1;
+ memcpy(loc_dev->name, "isp1763_otg", 11);
+ loc_dev->name[12] = '\0';
+
+ hal_init(("isp1763 OTG MEM Base= %lx irq = %x\n",
+ loc_dev->io_base, loc_dev->irq));
+
+#endif
+ /* bad pci latencies can contribute to overruns */
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+ if (latency) {
+ pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
+ if (limit && limit < latency) {
+ dbg("PCI latency reduced to max %d", limit);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
+ isp1763_pci_latency = limit;
+ } else {
+ /* it might already have been reduced */
+ isp1763_pci_latency = latency;
+ }
+ }
+
+ /* Try to check whether we can access Scratch Register of
+ * Host Controller or not. The initial PCI access is retried until
+ * local init for the PCI bridge is completed
+ */
+
+ loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
+ retry_count = PCI_ACCESS_RETRY_COUNT;
+ reg_data = 0;
+
+// while (reg_data < 0xFFFF)
+ {
+ u16 ureadVal = 0;
+ /*by default host is in 16bit mode, so
+ * io operations at this stage must be 16 bit
+ * */
+ isp1763_reg_write16(loc_dev, HC_SCRATCH_REG, reg_data);
+
+ udelay(1);
+ {
+ u32 chipid = 0;
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ if (chipid != ISP1763_CHIPID) {
+ printk(KERN_NOTICE
+ "CHIP ID WRONG: 0x%X at No. %d\n",
+ chipid, reg_data);
+ }
+ }
+ udelay(1);
+ ureadVal =
+ isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, ureadVal);
+ if (reg_data != ureadVal) {
+ printk(KERN_NOTICE
+ "MisMatch Scratch Value %x ActVal %x\n",
+ ureadVal, reg_data);
+ }
+
+ reg_data++;
+
+ }
+
+ chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
+ hal_init(KERN_NOTICE "isp1763_pci_probe:read chipid: 0x%X \n", chipid);
+
+ memcpy(loc_dev->name, ISP1763_DRIVER_NAME, sizeof(ISP1763_DRIVER_NAME));
+ loc_dev->name[sizeof(ISP1763_DRIVER_NAME)] = 0;
+ loc_dev->active = 1;
+
+ info("controller address %p\n", &dev->dev);
+ /*keep a copy of pcidevice */
+ loc_dev->pcidev = dev;
+
+ pci_set_master(dev);
+ pci_set_drvdata(dev, loc_dev);
+ hal_init(KERN_NOTICE "-isp1763_pci_probe \n");
+ hal_entry("%s: Exit\n", __FUNCTION__);
+ /* PLX DMA Test */
+#ifdef ENABLE_PLX_DMA
+
+ g_pDMA_Read_Buf = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+ g_pDMA_Write_Buf = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);
+
+ if (g_pDMA_Read_Buf == NULL || g_pDMA_Write_Buf == NULL) {
+ printk("Cannot allocate memory for DMA operations!\n");
+ return -2;
+ }
+#endif
+ return 1;
+
+
+ release_mem_region(pci_io_base, iolength);
+ iounmap(iobase);
+ release_mem_region(loc_dev->io_base, loc_dev->io_len);
+ iounmap(loc_dev->baseaddress);
+ hal_entry("%s: Exit\n", __FUNCTION__);
+
+ return status;
+} /* End of isp1763_pci_probe */
+#endif
+
+
+/*--------------------------------------------------------------*
+ *
+ * Module details: isp1763_pci_remove
+ *
+ * PCI cleanup function of ISP1763
+ * This function is called from PCI Driver as an removal function
+ * in the absence of PCI device or a de-registration of driver.
+ * This functions checks the registerd drivers (HCD, DCD, OTG) and calls
+ * the corresponding removal functions. Also initializes the local variables
+ * to zero.
+ *
+ * Input:
+ * struct pci_dev *dev ----> PCI Devie data structure
+ *
+ * Output void
+ *
+ * Called by: system function module_cleanup
+ *
+ *
+ *
+ --------------------------------------------------------------*/
+#ifdef NON_PCI
+static void __devexit isp1763_remove (struct device *dev)
+{
+ struct isp1763_dev *loc_dev;
+
+ if (dev != NULL)
+ hal_init(("isp1763_pci_remove(dev=%p)\n",dev));
+ else
+ hal_init("dev is NULL\n");
+
+ /*Lets handle the host first*/
+ loc_dev = &isp1763_loc_dev[ISP1763_HC];
+
+ /*free the memory occupied by host*/
+ release_mem_region(loc_dev->io_base, loc_dev->io_len);
+
+ /*unmap the occupied memory resources*/
+// iounmap(loc_dev->baseaddress); // jimmy
+
+ return;
+} /* End of isp1763_remove */
+#else /*PCI*/
+static void __devexit
+isp1763_pci_remove(struct pci_dev *dev)
+{
+ struct isp1763_dev *loc_dev;
+ hal_init(("isp1763_pci_remove(dev=%p)\n", dev));
+#ifdef ENABLE_PLX_DMA
+ if (g_pDMA_Read_Buf != NULL){
+ kfree(g_pDMA_Read_Buf);
+ }
+ if (g_pDMA_Write_Buf != NULL){
+ kfree(g_pDMA_Write_Buf);
+ }
+#endif
+ /*Lets handle the host first */
+ loc_dev = &isp1763_loc_dev[ISP1763_HC];
+ /*free the memory occupied by host */
+ release_mem_region(loc_dev->io_base, loc_dev->io_len);
+ release_mem_region(pci_io_base, iolength);
+ /*unmap the occupied memory resources */
+ iounmap(loc_dev->baseaddress);
+ /* unmap the occupied io resources */
+ iounmap(iobase);
+ return;
+} /* End of isp1763_pci_remove */
+#endif
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+
+#ifdef NON_PCI
+module_init (isp1763_module_init);
+module_exit (isp1763_module_cleanup);
+#else /*PCI*/
+module_init(isp1763_pci_module_init);
+module_exit(isp1763_pci_module_cleanup);
+#endif
diff --git a/drivers/usb/host/isp1763-hal.h b/drivers/usb/host/isp1763-hal.h new file mode 100644 index 00000000000..c1847ca464f --- /dev/null +++ b/drivers/usb/host/isp1763-hal.h @@ -0,0 +1,85 @@ +/*
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010
+*
+* ISP1763 Linux OTG Controller driver : hal
+*
+* 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.
+*
+* This is a hardware abstraction layer header file.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef HAL_X86_H
+#define HAL_X86_H
+
+#define DRIVER_AUTHOR "ST-ERICSSON "
+#define DRIVER_DESC "ISP1763 bus driver"
+
+/* Driver tuning, per ST-ERICSSON requirements: */
+
+#define MEM_TO_CHECK 4096 /*bytes, must be multiple of 2 */
+
+/* BIT defines */
+#define BIT0 (1 << 0)
+#define BIT1 (1 << 1)
+#define BIT2 (1 << 2)
+#define BIT3 (1 << 3)
+#define BIT4 (1 << 4)
+#define BIT5 (1 << 5)
+#define BIT6 (1 << 6)
+#define BIT7 (1 << 7)
+#define BIT8 (1 << 8)
+#define BIT9 (1 << 9)
+#define BIT10 (1 << 10)
+#define BIT11 (1 << 11)
+#define BIT12 (1 << 12)
+#define BIT13 (1 << 13)
+#define BIT14 (1 << 14)
+#define BIT15 (1 << 15)
+#define BIT16 (1 << 16)
+#define BIT17 (1 << 17)
+#define BIT18 (1 << 18)
+#define BIT19 (1 << 19)
+#define BIT20 (1 << 20)
+#define BIT21 (1 << 21)
+#define BIT22 (1 << 22)
+#define BIT23 (1 << 23)
+#define BIT24 (1 << 24)
+#define BIT25 (1 << 26)
+#define BIT27 (1 << 27)
+#define BIT28 (1 << 28)
+#define BIT29 (1 << 29)
+#define BIT30 (1 << 30)
+#define BIT31 (1 << 31)
+
+/* Definitions Related to Chip Address and CPU Physical Address
+ * cpu_phy_add: CPU Physical Address , it uses 32 bit data per address
+ * chip_add : Chip Address, it uses double word(64) bit data per address
+ */
+#define chip_add(cpu_phy_add) (((cpu_phy_add) - 0x400) / 8)
+#define cpu_phy_add(chip_add) ((8 * (chip_add)) + 0x400)
+
+/* for getting end add, and start add, provided we have one address with us */
+/* IMPORTANT length hex(base16) and dec(base10) works fine*/
+#define end_add(start_add, length) (start_add + (length - 4))
+#define start_add(end_add, length) (end_add - (length - 4))
+
+/* Device Registers*/
+#define DEV_UNLOCK_REGISTER 0x7C
+#define DEV_INTERRUPT_REGISTER 0x18
+#define INT_ENABLE_REGISTER 0x14
+
+#endif /*_HAL_X86_H_ */
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); diff --git a/drivers/usb/host/isp1763-hcd.h b/drivers/usb/host/isp1763-hcd.h new file mode 100644 index 00000000000..2361952b0f5 --- /dev/null +++ b/drivers/usb/host/isp1763-hcd.h @@ -0,0 +1,755 @@ +/* +* 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 file ~/drivers/usb/host/ehci-dbg.h for copyright owners (kernel version 2.6.9) +* Code is modified for ST-Ericsson product +* +* Author : wired support <wired.support@stericsson.com> +* +*/ + +#ifndef __PEHCI_H__ +#define __PEHCI_H__ + + +#define DRIVER_AUTHOR "ST-ERICSSON " +#define DRIVER_DESC "ISP1763 'Enhanced' Host Controller (EHCI) Driver" + +/* bus related stuff */ +#define __ACTIVE 0x01 +#define __SLEEPY 0x02 +#define __SUSPEND 0x04 +#define __TRANSIENT 0x80 + +#define USB_STATE_HALT 0 +#define USB_STATE_RUNNING (__ACTIVE) +#define USB_STATE_READY (__ACTIVE|__SLEEPY) +#define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) +#define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT) +#define USB_STATE_SUSPENDED (__SUSPEND) + +/* System flags */ +#define HCD_MEMORY 0x0001 +#define HCD_USB2 0x0020 +#define HCD_USB11 0x0010 + +#define HCD_IS_RUNNING(state) ((state) & __ACTIVE) +#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND) + + +/*--------------------------------------------------- + * Host controller related + -----------------------------------------------------*/ +/* IRQ line for the ISP1763 */ +#define HCD_IRQ IRQ_GPIO(25) +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ +#define STS_PCD (1<<2) /* port change detect */ +/* NOTE: urb->transfer_flags expected to not use this bit !!! */ +#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ + +/* Bits definations for qha*/ +/* Bits PID*/ +#define SETUP_PID (2) +#define OUT_PID (0) +#define IN_PID (1) + +/* Bits MULTI*/ +#define MULTI(x) ((x)<< 29) +#define XFER_PER_UFRAME(x) (((x) >> 29) & 0x3) + +/*Active, EP type and speed bits */ +#define QHA_VALID (1<<0) +#define QHA_ACTIVE (1<<31) + +/*1763 error bit maps*/ +#define HC_MSOF_INT (1<< 0) +#define HC_MSEC_INT (1 << 1) +#define HC_EOT_INT (1 << 3) +#define HC_OPR_REG_INT (1<<4) +#define HC_CLK_RDY_INT (1<<6) +#define HC_INTL_INT (1 << 7) +#define HC_ATL_INT (1 << 8) +#define HC_ISO_INT (1 << 9) +#define HC_OTG_INT (1 << 10) + +/*PTD error codes*/ +#define PTD_STATUS_HALTED (1 << 30) +#define PTD_XACT_ERROR (1 << 28) +#define PTD_BABBLE (1 << 29) +#define PTD_ERROR (PTD_STATUS_HALTED | PTD_XACT_ERROR | PTD_BABBLE) +/*ep types*/ +#define EPTYPE_BULK (2 << 12) +#define EPTYPE_CONTROL (0 << 12) +#define EPTYPE_INT (3 << 12) +#define EPTYPE_ISO (1 << 12) + +#define PHCI_QHA_LENGTH 32 + +#define usb_inc_dev_use usb_get_dev +#define usb_dec_dev_use usb_put_dev +#define usb_free_dev usb_put_dev +/*1763 host controller periodic size*/ +#define PTD_PERIODIC_SIZE 16 +#define MAX_PERIODIC_SIZE 16 +#define PTD_FRAME_MASK 0x1f +/*periodic list*/ +struct _periodic_list { + int framenumber; + struct list_head sitd_itd_head; + char high_speed; /*1 - HS ; 0 - FS*/ + u16 ptdlocation; +}; +typedef struct _periodic_list periodic_list; + + +/*iso ptd*/ +struct _isp1763_isoptd { + u32 td_info1; + u32 td_info2; + u32 td_info3; + u32 td_info4; + u32 td_info5; + u32 td_info6; + u32 td_info7; + u32 td_info8; +} __attribute__ ((aligned(32))); + +typedef struct _isp1763_isoptd isp1763_isoptd; + +struct _isp1763_qhint { + u32 td_info1; + u32 td_info2; + u32 td_info3; + u32 td_info4; + u32 td_info5; +#define INT_UNDERRUN (1 << 2) +#define INT_BABBLE (1 << 1) +#define INT_EXACT (1 << 0) + u32 td_info6; + u32 td_info7; + u32 td_info8; +} __attribute__ ((aligned(32))); + +typedef struct _isp1763_qhint isp1763_qhint; + + +struct _isp1763_qha { + u32 td_info1; /* First 32 bit */ + u32 td_info2; /* Second 32 bit */ + u32 td_info3; /* third 32 bit */ + u32 td_info4; /* fourth 32 bit */ + u32 reserved[4]; +}; +typedef struct _isp1763_qha isp1763_qha, *pisp1763_qha; + + + + +/*this does not cover all interrupts in 1763 chip*/ +typedef struct _ehci_regs { + + /*standard ehci registers */ + u32 command; + u32 usbinterrupt; + u32 usbstatus; + u32 hcsparams; + u32 frameindex; + + /*isp1763 interrupt specific registers */ + u16 hwmodecontrol; + u16 interrupt; + u16 interruptenable; + u32 interruptthreshold; + u16 iso_irq_mask_or; + u16 int_irq_mask_or; + u16 atl_irq_mask_or; + u16 iso_irq_mask_and; + u16 int_irq_mask_and; + u16 atl_irq_mask_and; + u16 buffer_status; + + /*isp1763 initialization registers */ + u32 reset; + u32 configflag; + u32 ports[4]; + u32 pwrdwn_ctrl; + + /*isp1763 transfer specific registers */ + u16 isotddonemap; + u16 inttddonemap; + u16 atltddonemap; + u16 isotdskipmap; + u16 inttdskipmap; + u16 atltdskipmap; + u16 isotdlastmap; + u16 inttdlastmap; + u16 atltdlastmap; + u16 scratch; + +} ehci_regs, *pehci_regs; + +/*memory management structures*/ +#define MEM_KV +#ifdef MEM_KV +typedef struct isp1763_mem_addr { + u32 phy_addr; /* Physical address of the memory */ + u32 virt_addr; /* after ioremap() function call */ + u8 num_alloc; /* In case n*smaller size is allocated then for clearing purpose */ + u32 blk_size; /*block size */ + u8 blk_num; /* number of the block */ + u8 used; /*used/free */ +} isp1763_mem_addr_t; +#else +typedef struct isp1763_mem_addr { + void *phy_addr; /* Physical address of the memory */ + void *virt_addr; /* after ioremap() function call */ + u8 usage; + u32 blk_size; /*block size */ +} isp1763_mem_addr_t; + +#endif +/* type tag from {qh,itd,sitd,fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1) +#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) +#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1) +#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1) + +/*next queuehead in execution*/ +#define QH_NEXT(dma) cpu_to_le32((u32)dma) + +struct ehci_qh { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.6.1 */ + u32 hw_info1; /* see EHCI 3.6.2 */ + + u32 hw_info2; /* see EHCI 3.6.2 */ + u32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + u32 hw_qtd_next; + u32 hw_alt_next; + u32 hw_token; + u32 hw_buf[5]; + u32 hw_buf_hi[5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; + struct ehci_qh *reclaim; /* next to reclaim */ + + atomic_t refcount; + wait_queue_head_t waitforcomplete; + unsigned stamp; + + u8 qh_state; + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ + u8 datatoggle; /*data toggle */ + + /*handling the ping stuffs */ + u8 ping; /*ping bit */ + + /*qtd <-> ptd management */ + + u32 qtd_ptd_index; /* Td-PTD map index for this ptd */ + u32 type; /* endpoint type */ + + /*iso stuffs */ + struct usb_host_endpoint *ep; + int next_uframe; /*next uframe for this endpoint */ + struct list_head itd_list; /*list of tds to this endpoint */ + isp1763_mem_addr_t memory_addr; + struct _periodic_list periodic_list; + /*scheduling requirements for this endpoint */ + u32 ssplit; + u32 csplit; + u8 totalptds; // total number of PTDs needed for current URB + u8 actualptds; // scheduled PTDs until now for current URB +}; + +/* urb private part for the driver. */ +typedef struct { + struct ehci_qh *qh; + u16 length; /* number of tds associated with this request */ + u16 td_cnt; /* number of tds already serviced */ + int state; /* State machine state when URB is deleted */ + int timeout; /* timeout for bulk transfers */ + wait_queue_head_t wait; /* wait State machine state when URB is deleted */ + /*FIX solve the full speed dying */ + struct timer_list urb_timer; + struct list_head qtd_list; + struct ehci_qtd *qtd[0]; /* list pointer to all corresponding TDs associated with this request */ + +} urb_priv_t; + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + + +/*Defination required for the ehci Queuehead */ +#define QH_HEAD 0x00008000 +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ +#define QH_STATE_TAKE_NEXT 8 /*take the new transfer from */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + + +#define EHCI_ITD_TRANLENGTH 0x0fff0000 /*transaction length */ +#define EHCI_ITD_PG 0x00007000 /*page select */ +#define EHCI_ITD_TRANOFFSET 0x00000fff /*transaction offset */ +#define EHCI_ITD_BUFFPTR 0xfffff000 /*buffer pointer */ + +struct ehci_sitd { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.3.1 */ + u32 hw_transaction[8]; /* see EHCI 3.3.2 */ +#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ +#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ + +#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ + + u32 hw_bufp[7]; /* see EHCI 3.3.3 */ + u32 hw_bufp_hi[7]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t sitd_dma; /* for this itd */ + struct urb *urb; + struct list_head sitd_list; /* list of urb frames' itds */ + dma_addr_t buf_dma; /* frame's buffer address */ + + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; + /*memory address */ + struct isp1763_mem_addr mem_addr; + int length; + u32 framenumber; + u32 ptdframe; + int sitd_index; + /*scheduling fields */ + u32 ssplit; + u32 csplit; + u32 start_frame; +}; + +struct ehci_itd { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.3.1 */ + u32 hw_transaction[8]; /* see EHCI 3.3.2 */ +#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ +#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ +#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ +#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ + +#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ + + u32 hw_bufp[7]; /* see EHCI 3.3.3 */ + u32 hw_bufp_hi[7]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t itd_dma; /* for this itd */ + struct urb *urb; + struct list_head itd_list; /* list of urb frames' itds */ + dma_addr_t buf_dma; /* frame's buffer address */ + u8 num_of_pkts; /*number of packets for this ITD */ + /* for now, only one hw_transaction per itd */ + u32 transaction; + u16 index; /* in urb->iso_frame_desc */ + u16 uframe; /* in periodic schedule */ + u16 usecs; + /*memory address */ + struct isp1763_mem_addr mem_addr; + int length; + u32 multi; + u32 framenumber; + u32 ptdframe; + int itd_index; + /*scheduling fields */ + u32 ssplit; + u32 csplit; +}; + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct ehci_qtd { + /* first part defined by EHCI spec */ + u32 hw_next; /* see EHCI 3.5.1 */ + u32 hw_alt_next; /* see EHCI 3.5.2 */ + u32 hw_token; /* see EHCI 3.5.3 */ + + u32 hw_buf[5]; /* see EHCI 3.5.4 */ + u32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ + u32 state; /*state of the qtd */ +#define QTD_STATE_NEW 0x100 +#define QTD_STATE_DONE 0x200 +#define QTD_STATE_SCHEDULED 0x400 +#define QTD_STATE_LAST 0x800 + struct isp1763_mem_addr mem_addr; +}; + +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +struct _phci_driver; +struct _isp1763_hcd; +#define EHCI_MAX_ROOT_PORTS 1 + +#include <linux/usb/hcd.h> + +#define USBNET +#ifdef USBNET +struct isp1763_async_cleanup_urb { + struct list_head urb_list; + struct urb *urb; +}; +#endif + + +/*host controller*/ +typedef struct _phci_hcd { + + struct usb_hcd usb_hcd; + spinlock_t lock; + + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; + /* periodic schedule support */ + unsigned periodic_size; + int next_uframe; /* scan periodic, start here */ + int periodic_sched; /* periodic activity count */ + int periodic_more_urb; + struct usb_device *otgdev; /*otg deice, with address 2 */ + struct timer_list rh_timer; /* drives root hub */ + struct list_head dev_list; /* devices on this bus */ + struct list_head urb_list; /*iso testing */ + + /*msec break in interrupts */ + atomic_t nuofsofs; + atomic_t missedsofs; + + struct isp1763_dev *dev; + /*hw info */ + u8 *iobase; + u32 iolength; + u8 *plxiobase; + u32 plxiolength; + + int irq; /* irq allocated */ + int state; /*state of the host controller */ + unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; + ehci_regs regs; + + struct _isp1763_qha qha; + struct _isp1763_qhint qhint; + struct _isp1763_isoptd isotd; + + struct tasklet_struct tasklet; + /*this timer is going to run every 20 msec */ + struct timer_list watchdog; + void (*worker_function) (struct _phci_hcd * hcd); + struct _periodic_list periodic_list[PTD_PERIODIC_SIZE]; +#ifdef USBNET + struct isp1763_async_cleanup_urb cleanup_urb; +#endif +} phci_hcd, *pphci_hcd; + +/*usb_device->hcpriv, points to this structure*/ +typedef struct hcd_dev { + struct list_head dev_list; + struct list_head urb_list; +} hcd_dev; + +#define usb_hcd_to_pehci_hcd(hcd) container_of(hcd, struct _phci_hcd, usb_hcd) + +/*td allocation*/ +#ifdef CONFIG_PHCI_MEM_SLAB + +#define qha_alloc(t,c) kmem_cache_alloc(c,ALLOC_FLAGS) +#define qha_free(c,x) kmem_cache_free(c,x) +static kmem_cache_t *qha_cache, *qh_cache, *qtd_cache; +static int +phci_hcd_mem_init(void) +{ + /* qha TDs accessed by controllers and host */ + qha_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!qha_cache) { + printk("no TD cache?"); + return -ENOMEM; + } + + /* qh TDs accessed by controllers and host */ + qh_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!qh_cache) { + printk("no TD cache?"); + return -ENOMEM; + } + + /* qtd accessed by controllers and host */ + qtd_cache = kmem_cache_create("phci_ptd", sizeof(isp1763_qha), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!qtd_cache) { + printk("no TD cache?"); + return -ENOMEM; + } + return 0; +} +static void +phci_mem_cleanup(void) +{ + if (qha_cache && kmem_cache_destroy(qha_cache)) + err("td_cache remained"); + qha_cache = 0; +} +#else + +#define qha_alloc(t,c) kmalloc(t,ALLOC_FLAGS) +#define qha_free(c,x) kfree(x) +#define qha_cache 0 + + +#ifdef CONFIG_ISO_SUPPORT +/*memory constants*/ +#define BLK_128_ 2 +#define BLK_256_ 3 +#define BLK_1024_ 1 +#define BLK_2048_ 3 +#define BLK_4096_ 3 //1 +#define BLK_8196_ 0 //1 +#define BLK_TOTAL (BLK_128_+BLK_256_ + BLK_1024_ +BLK_2048_+ BLK_4096_+BLK_8196_) + +#define BLK_SIZE_128 128 +#define BLK_SIZE_256 256 +#define BLK_SIZE_1024 1024 +#define BLK_SIZE_2048 2048 +#define BLK_SIZE_4096 4096 +#define BLK_SIZE_8192 8192 + +#define COMMON_MEMORY 1 + +#else +#define BLK_256_ 8 +#define BLK_1024_ 6 +#define BLK_4096_ 3 +#define BLK_TOTAL (BLK_256_ + BLK_1024_ + BLK_4096_) +#define BLK_SIZE_256 256 +#define BLK_SIZE_1024 1024 +#define BLK_SIZE_4096 4096 +#endif +static void phci_hcd_mem_init(void); +static inline void +phci_mem_cleanup(void) +{ + return; +} + +#endif + +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ + +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +/*Legends, + * ATL control, bulk transfer + * INTL interrupt transfer + * ISTL iso transfer + * */ + +/*buffer(transfer) bitmaps*/ +#define ATL_BUFFER 0x1 +#define INT_BUFFER 0x2 +#define ISO_BUFFER 0x4 +#define BUFFER_MAP 0x7 + +/* buffer type for ST-ERICSSON HC */ +#define TD_PTD_BUFF_TYPE_ATL 0 /* ATL buffer */ +#define TD_PTD_BUFF_TYPE_INTL 1 /* INTL buffer */ +#define TD_PTD_BUFF_TYPE_ISTL 2 /* ISO buffer */ +#define TD_PTD_TOTAL_BUFF_TYPES (TD_PTD_BUFF_TYPE_ISTL +1) +/*maximum number of tds per transfer type*/ +#define TD_PTD_MAX_BUFF_TDS 16 + +/*invalid td index in the headers*/ +#define TD_PTD_INV_PTD_INDEX 0xFFFF +/*Host controller buffer defination*/ +#define INVALID_FRAME_NUMBER 0xFFFFFFFF +/*per td transfer size*/ +#define HC_ATL_PL_SIZE 4096 +#define HC_ISTL_PL_SIZE 1024 +#define HC_INTL_PL_SIZE 1024 + +/*TD_PTD_MAP states*/ +#define TD_PTD_NEW 0x0000 +#define TD_PTD_ACTIVE 0x0001 +#define TD_PTD_IDLE 0x0002 +#define TD_PTD_REMOVE 0x0004 +#define TD_PTD_RELOAD 0x0008 +#define TD_PTD_IN_SCHEDULE 0x0010 +#define TD_PTD_DONE 0x0020 + +#define PTD_RETRY(x) (((x) >> 23) & 0x3) +#define PTD_PID(x) (((x) >> 10) & (0x3)) +#define PTD_NEXTTOGGLE(x) (((x) >> 25) & (0x1)) +#define PTD_XFERRED_LENGTH(x) ((x) & 0x7fff) +#define PTD_XFERRED_NONHSLENGTH(x) ((x) & 0x7ff) +#define PTD_PING_STATE(x) (((x) >> 26) & (0x1)) + +/* urb state*/ +#define DELETE_URB 0x0008 +#define NO_TRANSFER_ACTIVE 0xFFFF +#define NO_TRANSFER_DONE 0x0000 +#define MAX_PTD_BUFFER_SIZE 4096 /*max ptd size */ + +/*information of the td in headers of host memory*/ +typedef struct td_ptd_map { + u32 state; /* ACTIVE, NEW, TO_BE_REMOVED */ + u8 datatoggle; /*to preserve the data toggle for ATL/ISTL transfers */ + u32 ptd_bitmap; /* Bitmap of this ptd in HC headers */ + u32 ptd_header_addr; /* headers address of this td */ + u32 ptd_data_addr; /*data address of this td to write in and read from */ + /*this is address is actual RAM address not the CPU address + * RAM address = (CPU ADDRESS-0x400) >> 3 + * */ + u32 ptd_ram_data_addr; + u8 lasttd; /*last td , complete the transfer */ + struct ehci_qh *qh; /* endpoint */ + struct ehci_qtd *qtd; /* qtds for this endpoint */ + struct ehci_itd *itd; /*itd pointer */ + struct ehci_sitd *sitd; /*itd pointer */ + /*iso specific only */ + u32 grouptdmap; /*if td need to complete with error, then process all the tds + in the groupmap */ +} td_ptd_map_t; + +/*buffer(ATL/ISTL/INTL) managemnet*/ +typedef struct td_ptd_map_buff { + u8 buffer_type; /* Buffer type: BUFF_TYPE_ATL/INTL/ISTL0/ISTL1 */ + u8 active_ptds; /* number of active td's in the buffer */ + u8 total_ptds; /* Total number of td's present in the buffer (active + tobe removed + skip) */ + u8 max_ptds; /* Maximum number of ptd's(32) this buffer can withstand */ + u16 active_ptd_bitmap; /* Active PTD's bitmap */ + u16 pending_ptd_bitmap; /* skip PTD's bitmap */ + td_ptd_map_t map_list[TD_PTD_MAX_BUFF_TDS]; /* td_ptd_map list */ +} td_ptd_map_buff_t; + + +#define USB_HCD_MAJOR 245 +#define USB_HCD_MODULE_NAME "isp1763hcd" + +#if 1 +static char devpath[] = "/dev/isp1763hcd"; +#endif + +#define HCD_IOC_MAGIC 'h' + +#define HCD_IOC_POWERDOWN _IO(HCD_IOC_MAGIC, 1) +#define HCD_IOC_POWERUP _IO(HCD_IOC_MAGIC, 2) +#define HCD_IOC_TESTSE0_NACK _IO(HCD_IOC_MAGIC, 3) +#define HCD_IOC_TEST_J _IO(HCD_IOC_MAGIC,4) +#define HCD_IOC_TEST_K _IO(HCD_IOC_MAGIC,5) +#define HCD_IOC_TEST_TESTPACKET _IO(HCD_IOC_MAGIC,6) +#define HCD_IOC_TEST_FORCE_ENABLE _IO(HCD_IOC_MAGIC,7) +#define HCD_IOC_TEST_SUSPEND_RESUME _IO(HCD_IOC_MAGIC,8) +#define HCD_IOC_TEST_SINGLE_STEP_GET_DEV_DESC _IO(HCD_IOC_MAGIC,9) +#define HCD_IOC_TEST_SINGLE_STEP_SET_FEATURE _IO(HCD_IOC_MAGIC,10) +#define HCD_IOC_TEST_STOP _IO(HCD_IOC_MAGIC,11) +#define HCD_IOC_SUSPEND_BUS _IO(HCD_IOC_MAGIC,12) +#define HCD_IOC_RESUME_BUS _IO(HCD_IOC_MAGIC,13) +#define HCD_IOC_REMOTEWAKEUP_BUS _IO(HCD_IOC_MAGIC,14) + +#define HOST_COMPILANCE_TEST_ENABLE 1 +#define HOST_COMP_TEST_SE0_NAK 1 +#define HOST_COMP_TEST_J 2 +#define HOST_COMP_TEST_K 3 +#define HOST_COMP_TEST_PACKET 4 +#define HOST_COMP_TEST_FORCE_ENABLE 5 +#define HOST_COMP_HS_HOST_PORT_SUSPEND_RESUME 6 +#define HOST_COMP_SINGLE_STEP_GET_DEV_DESC 7 +#define HOST_COMP_SINGLE_STEP_SET_FEATURE 8 + +#endif diff --git a/drivers/usb/host/isp1763-itdptd.c b/drivers/usb/host/isp1763-itdptd.c new file mode 100644 index 00000000000..8e0c16ee31f --- /dev/null +++ b/drivers/usb/host/isp1763-itdptd.c @@ -0,0 +1,2156 @@ +/*
+* 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.
+*
+* This is a host controller driver file. Isochronous event processing is handled here.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+#ifdef CONFIG_ISO_SUPPORT
+void phcd_clean_periodic_ep(void);
+#endif
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define MAX_URBS 8
+#define MAX_EPS 2/*maximum 2 endpoints supported in ISO transfers.*/
+/*number of microframe per frame which is scheduled, for high speed device
+* actually , NUMMICROFRAME should be 8 , but the micro frame #7 is fail , so
+* there's just 4 microframe is used (#0 -> #4)
+* Writer : LyNguyen - 25Nov09
+*/
+#define NUMMICROFRAME 8
+struct urb *gstUrb_pending[MAX_URBS] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+struct usb_host_endpoint *periodic_ep[MAX_EPS];
+
+int giUrbCount = 0; /* count the pending urb*/
+int giUrbIndex = 0; /*the index of urb need to be scheduled next*/
+/*
+ * phcd_iso_sitd_to_ptd - convert an SITD into a PTD
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more specific elements.
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * void * ptd
+ * - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ * -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_sitd_to_ptd(phci_hcd * hcd,
+ struct ehci_sitd *sitd, struct urb *urb, void *ptd)
+{
+ struct _isp1763_isoptd *iso_ptd;
+ struct isp1763_mem_addr *mem_addr;
+
+ unsigned long max_packet, mult, length, td_info1, td_info3;
+ unsigned long token, port_num, hub_num, data_addr;
+ unsigned long frame_number;
+
+ iso_dbg(ISO_DBG_ENTRY, "phcd_iso_sitd_to_ptd entry\n");
+
+ /* Variable initialization */
+ iso_ptd = (struct _isp1763_isoptd *) ptd;
+ mem_addr = &sitd->mem_addr;
+
+ /*
+ * For both ISO and INT endpoints descriptors, new bit fields we added to
+ * specify whether or not the endpoint supports high bandwidth, and if so
+ * the number of additional packets that the endpoint can support during a
+ * single microframe.
+ * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+ * Valid values:
+ * 00 None (1 transaction/uFrame)
+ * 01 1 additional transaction
+ * 10 2 additional transactions
+ * 11 reserved
+ */
+ max_packet = usb_maxpacket(urb->dev, urb->pipe,usb_pipeout(urb->pipe));
+
+ /*
+ * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+ * zero (0).
+ */
+ mult = 1 + ((max_packet >> 11) & 0x3);
+ max_packet &= 0x7ff;
+
+ /* This is the size of the request (bytes to write or bytes to read) */
+ length = sitd->length;
+
+ /*
+ * Set V bit to indicate that there is payload to be sent or received. And
+ * indicate that the current PTD is active.
+ */
+ td_info1 = QHA_VALID;
+
+ /*
+ * Set the number of bytes that can be transferred by this PTD. This indicates
+ * the depth of the data field.
+ */
+ td_info1 |= (length << 3);
+
+ /*
+ * Set the maximum packet length which indicates the maximum number of bytes that
+ * can be sent to or received from the endpoint in a single data packet.
+ */
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ /*
+ * According to the ISP1763 specs for sITDs, OUT token max packet should
+ * not be more than 188 bytes, while IN token max packet not more than
+ * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+ */
+ if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+ iso_dbg(ISO_DBG_INFO,
+ "IN Max packet over maximum\n");
+ max_packet = 192;
+ }
+
+ if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+ iso_dbg(ISO_DBG_INFO,
+ "OUT Max packet over maximum\n");
+ max_packet = 188;
+ }
+ }
+ td_info1 |= (max_packet << 18);
+
+ /*
+ * Place the FIRST BIT of the endpoint number here.
+ */
+ td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+ /*
+ * Set the number of successive packets the HC can submit to the endpoint.
+ */
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ td_info1 |= MULTI(mult);
+ }
+
+ /* Set the first DWORD */
+ iso_ptd->td_info1 = td_info1;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+ iso_ptd->td_info1);
+
+ /*
+ * Since the first bit have already been added on the first DWORD of the PTD
+ * we only need to add the last 3-bits of the endpoint number.
+ */
+ token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+ /*
+ * Get the device address and set it accordingly to its assigned bits of the 2nd
+ * DWORD.
+ */
+ token |= usb_pipedevice(urb->pipe) << 3;
+
+ /* See a split transaction is needed */
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ /*
+ * If we are performing a SPLIT transaction indicate that it is so by setting
+ * the S bit of the second DWORD.
+ */
+ token |= 1 << 14;
+
+ port_num = urb->dev->ttport;
+ hub_num = urb->dev->tt->hub->devnum;
+
+ /* Set the the port number of the hub or embedded TT */
+ token |= port_num << 18;
+
+ /*
+ * Set the hub address, this should be zero for the internal or
+ * embedded hub
+ */
+ token |= hub_num << 25;
+ }
+
+ /* if(urb->dev->speed != USB_SPEED_HIGH) */
+ /*
+ * Determine if the direction of this pipe is IN, if so set the Token bit of
+ * the second DWORD to indicate it as IN. Since it is initialized to zero and
+ * zero indicates an OUT token, then we do not need anything to the Token bit
+ * if it is an OUT token.
+ */
+ if (usb_pipein(urb->pipe)) {
+ token |= (IN_PID << 10);
+ }
+
+ /* Set endpoint type to Isochronous */
+ token |= EPTYPE_ISO;
+
+ /* Set the second DWORD */
+ iso_ptd->td_info2 = token;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+ iso_ptd->td_info2);
+
+ /*
+ * Get the physical address of the memory location that was allocated for this PTD
+ * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+ * rev 3.01 page 17 to 18.
+ */
+ data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+ data_addr >>= 3;
+
+ /* Set it to its location in the third DWORD */
+ td_info3 =( 0xffff&data_addr) << 8;
+
+ /*
+ * Set the frame number when this PTD will be sent for ISO OUT or IN
+ * Bits 0 to 2 are don't care, only bits 3 to 7.
+ */
+ frame_number = sitd->framenumber;
+ frame_number = sitd->start_frame;
+ td_info3 |= (0xff& ((frame_number) << 3));
+
+ /* Set the third DWORD */
+ iso_ptd->td_info3 = td_info3;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+ iso_ptd->td_info3);
+
+ /*
+ * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+ * This have the same functionality with the V bit of DWORD0
+ */
+ iso_ptd->td_info4 = QHA_ACTIVE;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+ iso_ptd->td_info4);
+
+ /* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+ if (usb_pipein(urb->pipe)){
+ iso_ptd->td_info5 = (sitd->ssplit);
+ }else{
+ iso_ptd->td_info5 = (sitd->ssplit << 2);
+ }
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+ iso_ptd->td_info5);
+
+ /*
+ * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+ * This is VALID only for IN (since ISO transfers don't have handshake stages)
+ */
+ iso_ptd->td_info6 = sitd->csplit;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+ iso_ptd->td_info6);
+
+ /*printk(" [phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",iso_ptd->td_info1);
+ printk(" [phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",iso_ptd->td_info2);
+ printk(" [phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",iso_ptd->td_info3);
+ printk(" [phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",iso_ptd->td_info4);
+ printk(" [phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",iso_ptd->td_info5);
+ printk(" [phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",iso_ptd->td_info6);*/
+ iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+ return iso_ptd;
+}
+
+
+/*
+ * phcd_iso_itd_to_ptd - convert an ITD into a PTD
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct ehci_itd *itd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more ST-ERICSSON specific elements.
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * void * ptd
+ * - Points to the ISO ptd structure that needs to be initialized
+ *
+ * API Description
+ * This is mainly responsible for:
+ * -Initializing the PTD that will be used for the ISO transfer
+ */
+void *
+phcd_iso_itd_to_ptd(phci_hcd * hcd,
+ struct ehci_itd *itd, struct urb *urb, void *ptd)
+{
+ struct _isp1763_isoptd *iso_ptd;
+ struct isp1763_mem_addr *mem_addr;
+
+ unsigned long max_packet, mult, length, td_info1, td_info3;
+ unsigned long token, port_num, hub_num, data_addr;
+ unsigned long frame_number;
+ int maxpacket;
+ iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_to_ptd entry\n");
+
+ /* Variable initialization */
+ iso_ptd = (struct _isp1763_isoptd *) ptd;
+ mem_addr = &itd->mem_addr;
+
+ /*
+ * For both ISO and INT endpoints descriptors, new bit fields we added to
+ * specify whether or not the endpoint supports high bandwidth, and if so
+ * the number of additional packets that the endpoint can support during a
+ * single microframe.
+ * Bits 12:11 specify whether the endpoint supports high-bandwidth transfers
+ * Valid values:
+ * 00 None (1 transaction/uFrame)
+ * 01 1 additional transaction
+ * 10 2 additional transactions
+ * 11 reserved
+ */
+ max_packet = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ /*
+ * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+ * zero (0).
+ */
+ maxpacket &= 0x7ff;
+ mult = 1 + ((max_packet >> 11) & 0x3);
+
+
+ max_packet &= 0x7ff;
+
+ /* This is the size of the request (bytes to write or bytes to read) */
+ length = itd->length;
+
+ /*
+ * Set V bit to indicate that there is payload to be sent or received. And
+ * indicate that the current PTD is active.
+ */
+ td_info1 = QHA_VALID;
+
+ /*
+ * Set the number of bytes that can be transferred by this PTD. This indicates
+ * the depth of the data field.
+ */
+ td_info1 |= (length << 3);
+
+ /*
+ * Set the maximum packet length which indicates the maximum number of bytes that
+ * can be sent to or received from the endpoint in a single data packet.
+ */
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ /*
+ * According to the ISP1763 specs for sITDs, OUT token max packet should
+ * not be more than 188 bytes, while IN token max packet not more than
+ * 192 bytes (ISP1763 Rev 3.01, Table 72, page 79
+ */
+ if (usb_pipein(urb->pipe) && (max_packet > 192)) {
+ iso_dbg(ISO_DBG_INFO,
+ "[phcd_iso_itd_to_ptd]: IN Max packet over maximum\n");
+ max_packet = 192;
+ }
+
+ if ((!usb_pipein(urb->pipe)) && (max_packet > 188)) {
+ iso_dbg(ISO_DBG_INFO,
+ "[phcd_iso_itd_to_ptd]: OUT Max packet over maximum\n");
+ max_packet = 188;
+ }
+ } else { /*HIGH SPEED */
+
+ if (max_packet > 1024){
+ max_packet = 1024;
+ }
+ }
+ td_info1 |= (max_packet << 18);
+
+ /*
+ * Place the FIRST BIT of the endpoint number here.
+ */
+ td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+
+ /*
+ * Set the number of successive packets the HC can submit to the endpoint.
+ */
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ td_info1 |= MULTI(mult);
+ }
+
+ /* Set the first DWORD */
+ iso_ptd->td_info1 = td_info1;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD0 = 0x%08x\n",
+ iso_ptd->td_info1);
+
+ /*
+ * Since the first bit have already been added on the first DWORD of the PTD
+ * we only need to add the last 3-bits of the endpoint number.
+ */
+ token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+
+ /*
+ * Get the device address and set it accordingly to its assigned bits of the 2nd
+ * DWORD.
+ */
+ token |= usb_pipedevice(urb->pipe) << 3;
+
+ /* See a split transaction is needed */
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ /*
+ * If we are performing a SPLIT transaction indicate that it is so by setting
+ * the S bit of the second DWORD.
+ */
+ token |= 1 << 14;
+
+ port_num = urb->dev->ttport;
+ hub_num = urb->dev->tt->hub->devnum;
+
+ /* Set the the port number of the hub or embedded TT */
+ token |= port_num << 18;
+
+ /*
+ * Set the hub address, this should be zero for the internal or
+ * embedded hub
+ */
+ token |= hub_num << 25;
+ }
+
+ /* if(urb->dev->speed != USB_SPEED_HIGH) */
+ /*
+ * Determine if the direction of this pipe is IN, if so set the Token bit of
+ * the second DWORD to indicate it as IN. Since it is initialized to zero and
+ * zero indicates an OUT token, then we do not need anything to the Token bit
+ * if it is an OUT token.
+ */
+ if (usb_pipein(urb->pipe)){
+ token |= (IN_PID << 10);
+ }
+
+ /* Set endpoint type to Isochronous */
+ token |= EPTYPE_ISO;
+
+ /* Set the second DWORD */
+ iso_ptd->td_info2 = token;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD1 = 0x%08x\n",
+ iso_ptd->td_info2);
+
+ /*
+ * Get the physical address of the memory location that was allocated for this PTD
+ * in the PAYLOAD region, using the formula indicated in sectin 7.2.2 of the ISP1763 specs
+ * rev 3.01 page 17 to 18.
+ */
+ data_addr = ((unsigned long) (mem_addr->phy_addr) & 0xffff) - 0x400;
+ data_addr >>= 3;
+
+ /* Set it to its location in the third DWORD */
+ td_info3 = (data_addr&0xffff) << 8;
+
+ /*
+ * Set the frame number when this PTD will be sent for ISO OUT or IN
+ * Bits 0 to 2 are don't care, only bits 3 to 7.
+ */
+ frame_number = itd->framenumber;
+ td_info3 |= (0xff&(frame_number << 3));
+
+ /* Set the third DWORD */
+ iso_ptd->td_info3 = td_info3;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD2 = 0x%08x\n",
+ iso_ptd->td_info3);
+
+ /*
+ * Set the A bit of the fourth DWORD to 1 to indicate that this PTD is active.
+ * This have the same functionality with the V bit of DWORD0
+ */
+ iso_ptd->td_info4 = QHA_ACTIVE;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD3 = 0x%08x\n",
+ iso_ptd->td_info4);
+
+ /* Set the fourth DWORD to specify which uSOFs the start split needs to be placed */
+ iso_ptd->td_info5 = itd->ssplit;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD4 = 0x%08x\n",
+ iso_ptd->td_info5);
+
+ /*
+ * Set the fifth DWORD to specify which uSOFs the complete split needs to be sent.
+ * This is VALID only for IN (since ISO transfers don't have handshake stages)
+ */
+ iso_ptd->td_info6 = itd->csplit;
+ iso_dbg(ISO_DBG_DATA, "[phcd_iso_itd_to_ptd]: DWORD5 = 0x%08x\n",
+ iso_ptd->td_info6);
+
+ iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_to_ptd exit\n");
+ return iso_ptd;
+} /* phcd_iso_itd_to_ptd */
+
+/*
+ * phcd_iso_scheduling_info - Initializing the start split and complete split.
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct ehci_qh *qhead
+ * - Contains information about the endpoint.
+ * unsigned long max_pkt
+ * - Maximum packet size that the endpoint in capable of handling
+ * unsigned long high_speed
+ * - Indicates if the bus is a high speed bus
+ * unsigned long ep_in
+ * - Inidcates if the endpoint is an IN endpoint
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Determining the number of start split needed during an OUT transaction or
+ * the number of complete splits needed during an IN transaction.
+ */
+unsigned long
+phcd_iso_scheduling_info(phci_hcd * hcd,
+ struct ehci_qh *qhead,
+ unsigned long max_pkt,
+ unsigned long high_speed, unsigned long ep_in)
+{
+ unsigned long count, usof, temp;
+
+ /* Local variable initialization */
+ usof = 0x1;
+
+ if (high_speed) {
+ qhead->csplit = 0;
+
+ /* Always send high speed transfers in first uframes */
+ qhead->ssplit = 0x1;
+ return 0;
+ }
+
+ /* Determine how many 188 byte-transfers are needed to send all data */
+ count = max_pkt / 188;
+
+ /*
+ * Check is the data is not a factor of 188, if it is not then we need
+ * one more 188 transfer to move the last set of data less than 188.
+ */
+ if (max_pkt % 188){
+ count += 1;
+ }
+
+ /*
+ * Remember that usof was initialized to 0x1 so that means
+ * that usof is always guranteed a value of 0x1 and then
+ * depending on the maxp, other bits of usof will also be set.
+ */
+ for (temp = 0; temp < count; temp++){
+ usof |= (0x1 << temp);
+ }
+
+ if (ep_in) {
+ /*
+ * Send start split into first frame.
+ */
+ qhead->ssplit = 0x1;
+
+ /*
+ * Inidicate that we can send a complete split starting from
+ * the third uFrame to how much complete split is needed to
+ * retrieve all data.
+ *
+ * Of course, the first uFrame is reserved for the start split, the
+ * second is reserved for the TT to send the request and get some
+ * data.
+ */
+ qhead->csplit = (usof << 2);
+ } else {
+ /*
+ * For ISO OUT we don't need to send out a complete split
+ * since we do not require and data coming in to us (since ISO
+ * do not have integrity checking/handshake).
+ *
+ * For start split we indicate that we send a start split from the
+ * first uFrame up to the the last uFrame needed to retrieve all
+ * data
+ */
+ qhead->ssplit = usof;
+ qhead->csplit = 0;
+ } /* else for if(ep_in) */
+ return 0;
+} /* phcd_iso_scheduling_info */
+
+/*
+ * phcd_iso_sitd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ * - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more specific elements.
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long packets
+ * - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ * > sitd->length = length; -- the size of the request
+ * > sitd->multi = multi; -- the number of transactions for
+ * this EP per micro frame
+ * > sitd->hw_bufp[0] = buf_dma; -- The base address of the buffer where
+ * to put the data (this base address was
+ * the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ * the requesting party will be placed or data requested by the requesting party will
+ * be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_sitd_fill(phci_hcd * hcd,
+ struct ehci_sitd *sitd,
+ struct urb *urb, unsigned long packets)
+{
+ unsigned long length, offset, pipe;
+ unsigned long max_pkt;
+ dma_addr_t buff_dma;
+ struct isp1763_mem_addr *mem_addr;
+
+#ifdef COMMON_MEMORY
+ struct ehci_qh *qhead = NULL;
+#endif
+
+ iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+ /*
+ * The value for both these variables are supplied by the one
+ * who submitted the URB.
+ */
+ length = urb->iso_frame_desc[packets].length;
+ offset = urb->iso_frame_desc[packets].offset;
+
+ /* Initialize the status and actual length of this packet */
+ urb->iso_frame_desc[packets].actual_length = 0;
+ urb->iso_frame_desc[packets].status = -EXDEV;
+
+ /* Buffer for this packet */
+ buff_dma = (u32) ((unsigned char *) urb->transfer_buffer + offset);
+
+ /* Memory for this packet */
+ mem_addr = &sitd->mem_addr;
+
+ pipe = urb->pipe;
+ max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+ max_pkt = max_pkt & 0x7FF;
+
+ if ((length < 0) || (max_pkt < length)) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No available memory.\n");
+ return -ENOSPC;
+ }
+ sitd->buf_dma = buff_dma;
+
+
+#ifndef COMMON_MEMORY
+ /*
+ * Allocate memory in the PAYLOAD memory region for the
+ * data buffer for this SITD
+ */
+ phci_hcd_mem_alloc(length, mem_addr, 0);
+ if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+ mem_addr = 0;
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No payload memory available\n");
+ return -ENOMEM;
+ }
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ qhead=urb->hcpriv;
+#else
+ qhead = urb->ep->hcpriv;
+#endif
+ if (qhead) {
+
+ mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+ mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+ } else {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No payload memory available\n");
+ return -ENOMEM;
+ }
+
+
+#endif
+ /* Length of this packet */
+ sitd->length = length;
+
+ /* Buffer address, one ptd per packet */
+ sitd->hw_bufp[0] = buff_dma;
+
+ iso_dbg(ISO_DBG_EXIT, "phcd_iso_sitd_fill exit\n");
+ return 0;
+}
+
+/*
+ * phcd_iso_itd_fill - Allocate memory from the PAYLOAD memory region
+ *
+ * phci_hcd *pHcd_st
+ * - Main host controller driver structure
+ * struct ehci_itd *itd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more IC specific elements.
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long packets
+ * - Total number of packets to completely transfer this ISO transfer request.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Initialize the following elements of the ITS structure
+ * > itd->length = length; -- the size of the request
+ * > itd->multi = multi; -- the number of transactions for
+ * this EP per micro frame
+ * > itd->hw_bufp[0] = buf_dma; -- The base address of the buffer where
+ * to put the data (this base address was
+ * the buffer provided plus the offset)
+ * - Allocating memory from the PAYLOAD memory area, where the data coming from
+ * the requesting party will be placed or data requested by the requesting party will
+ * be retrieved when it is available.
+ */
+unsigned long
+phcd_iso_itd_fill(phci_hcd * hcd,
+ struct ehci_itd *itd,
+ struct urb *urb,
+ unsigned long packets, unsigned char numofPkts)
+{
+ unsigned long length, offset, pipe;
+ unsigned long max_pkt, mult;
+ dma_addr_t buff_dma;
+ struct isp1763_mem_addr *mem_addr;
+#ifdef COMMON_MEMORY
+ struct ehci_qh *qhead = NULL;
+#endif
+ int i = 0;
+
+ iso_dbg(ISO_DBG_ENTRY, "phcd_iso_itd_fill entry\n");
+ for (i = 0; i < 8; i++){
+ itd->hw_transaction[i] = 0;
+ }
+ /*
+ * The value for both these variables are supplied by the one
+ * who submitted the URB.
+ */
+ length = urb->iso_frame_desc[packets].length;
+ offset = urb->iso_frame_desc[packets].offset;
+
+ /* Initialize the status and actual length of this packet */
+ urb->iso_frame_desc[packets].actual_length = 0;
+ urb->iso_frame_desc[packets].status = -EXDEV;
+
+ /* Buffer for this packet */
+ buff_dma = cpu_to_le32((unsigned char *) urb->transfer_buffer + offset);
+
+ /* Memory for this packet */
+ mem_addr = &itd->mem_addr;
+
+ pipe = urb->pipe;
+ max_pkt = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
+
+ mult = 1 + ((max_pkt >> 11) & 0x3);
+ max_pkt = max_pkt & 0x7FF;
+ max_pkt *= mult;
+
+ if ((length < 0) || (max_pkt < length)) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No available memory.\n");
+ return -ENOSPC;
+ }
+ itd->buf_dma = buff_dma;
+ for (i = packets + 1; i < numofPkts + packets; i++)
+ length += urb->iso_frame_desc[i].length;
+
+ /*
+ * Allocate memory in the PAYLOAD memory region for the
+ * data buffer for this ITD
+ */
+#ifndef COMMON_MEMORY
+
+ phci_hcd_mem_alloc(length, mem_addr, 0);
+ if (length && ((mem_addr->phy_addr == 0) || (mem_addr->virt_addr == 0))) {
+ mem_addr = 0;
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No payload memory available\n");
+ return -ENOMEM;
+ }
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ qhead = urb->ep->hcpriv;
+#else
+ qhead=urb->hcpriv;
+#endif
+ if (qhead) {
+
+ mem_addr->phy_addr = qhead->memory_addr.phy_addr + offset;
+
+ mem_addr->virt_addr = qhead->memory_addr.phy_addr + offset;
+ } else {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_fill Error]: No payload memory available\n");
+ return -ENOMEM;
+ }
+
+
+#endif
+ /* Length of this packet */
+ itd->length = length;
+
+ /* Number of transaction per uframe */
+ itd->multi = mult;
+
+ /* Buffer address, one ptd per packet */
+ itd->hw_bufp[0] = buff_dma;
+
+ iso_dbg(ISO_DBG_EXIT, "phcd_iso_itd_fill exit\n");
+ return 0;
+} /* phcd_iso_itd_fill */
+
+/*
+ * phcd_iso_get_sitd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct ehci_sitd *sitd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ * in the bitmap so that this PTD will be included into
+ * the periodic schedule
+ */
+void
+phcd_iso_get_sitd_ptd_index(phci_hcd * hcd, struct ehci_sitd *sitd)
+{
+ td_ptd_map_buff_t *ptd_map_buff;
+ unsigned long buff_type, max_ptds;
+ unsigned char sitd_index, bitmap;
+
+ /* Local variable initialization */
+ bitmap = 0x1;
+ buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+ ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+ max_ptds = ptd_map_buff->max_ptds;
+ sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+ for (sitd_index = 0; sitd_index < max_ptds; sitd_index++) {
+ /*
+ * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+ */
+ if (ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) {
+ iso_dbg(ISO_DBG_INFO,
+ "[phcd_iso_get_itd_ptd_index] There's a free PTD No. %d\n",
+ sitd_index);
+ /*
+ * Determine if this is a newly allocated SITD by checking the
+ * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+ * initialization
+ */
+ if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+ sitd->sitd_index = sitd_index;
+ }
+
+ /* Once there is a free slot, indicate that it is already taken */
+ ptd_map_buff->map_list[sitd_index].datatoggle = 0;
+ ptd_map_buff->map_list[sitd_index].state =
+ TD_PTD_ACTIVE;
+ ptd_map_buff->map_list[sitd_index].qtd = NULL;
+
+ /* Put a connection to the SITD with the PTD maplist */
+ ptd_map_buff->map_list[sitd_index].sitd = sitd;
+ ptd_map_buff->map_list[sitd_index].itd = NULL;
+ ptd_map_buff->map_list[sitd_index].qh = NULL;
+
+ /* ptd_bitmap just holds the bit assigned to this PTD. */
+ ptd_map_buff->map_list[sitd_index].ptd_bitmap =
+ bitmap << sitd_index;
+
+ phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+ map_list[sitd_index], sitd->sitd_index,
+ buff_type);
+
+ /*
+ * Indicate that this SITD is the last in the list and update
+ * the number of active PTDs
+ */
+ ptd_map_buff->map_list[sitd_index].lasttd = 0;
+ ptd_map_buff->total_ptds++;
+
+
+ ptd_map_buff->active_ptd_bitmap |=
+ (bitmap << sitd_index);
+ ptd_map_buff->pending_ptd_bitmap |= (bitmap << sitd_index);
+ break;
+ } /* if(ptd_map_buff->map_list[sitd_index].state == TD_PTD_NEW) */
+ } /* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+ return;
+}
+
+/*
+ * phcd_iso_get_itd_ptd_index - Allocate an ISO PTD from the ISO PTD map list
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct ehci_itd *itd
+ * - Isochronous Transfer Descriptor, contains elements as defined by the
+ * EHCI standard plus a few more IC specific elements.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating an ISO PTD from the ISO PTD map list
+ * - Set the equivalent bit of the allocated PTD to active
+ * in the bitmap so that this PTD will be included into
+ * the periodic schedule
+ */
+void
+phcd_iso_get_itd_ptd_index(phci_hcd * hcd, struct ehci_itd *itd)
+{
+ td_ptd_map_buff_t *ptd_map_buff;
+ unsigned long buff_type, max_ptds;
+ unsigned char itd_index, bitmap;
+
+ /* Local variable initialization */
+ bitmap = 0x1;
+ buff_type = td_ptd_pipe_x_buff_type[TD_PTD_BUFF_TYPE_ISTL];
+ ptd_map_buff = (td_ptd_map_buff_t *) & (td_ptd_map_buff[buff_type]);
+ max_ptds = ptd_map_buff->max_ptds;
+
+ itd->itd_index = TD_PTD_INV_PTD_INDEX;
+
+ for (itd_index = 0; itd_index < max_ptds; itd_index++) {
+ /*
+ * ISO have 32 PTDs, the first thing to do is look for a free PTD.
+ */
+ if (ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) {
+ /*
+ * Determine if this is a newly allocated ITD by checking the
+ * itd_index, since it was set to TD_PTD_INV_PTD_INDEX during
+ * initialization
+ */
+ if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+ itd->itd_index = itd_index;
+ }
+
+ /* Once there is a free slot, indicate that it is already taken */
+ ptd_map_buff->map_list[itd_index].datatoggle = 0;
+ ptd_map_buff->map_list[itd_index].state = TD_PTD_ACTIVE;
+ ptd_map_buff->map_list[itd_index].qtd = NULL;
+
+ /* Put a connection to the ITD with the PTD maplist */
+ ptd_map_buff->map_list[itd_index].itd = itd;
+ ptd_map_buff->map_list[itd_index].qh = NULL;
+
+ /* ptd_bitmap just holds the bit assigned to this PTD. */
+ ptd_map_buff->map_list[itd_index].ptd_bitmap =
+ bitmap << itd_index;
+
+ phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+ map_list[itd_index],
+ itd->itd_index, buff_type);
+
+ /*
+ * Indicate that this ITD is the last in the list and update
+ * the number of active PTDs
+ */
+ ptd_map_buff->map_list[itd_index].lasttd = 0;
+ ptd_map_buff->total_ptds++;
+
+ ptd_map_buff->active_ptd_bitmap |=
+ (bitmap << itd_index);
+ ptd_map_buff->pending_ptd_bitmap |= (bitmap << itd_index);
+ break;
+ } /* if(ptd_map_buff->map_list[itd_index].state == TD_PTD_NEW) */
+ } /* for(itd_index = 0; itd_index < max_ptds; itd_index++) */
+ return;
+} /* phcd_iso_get_itd_ptd_index */
+
+/*
+ * phcd_iso_sitd_free_list - Free memory used by SITDs in SITD list
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long status
+ * - Variable provided by the calling routine that contain the status of the
+ * SITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Cleaning up memory used by each SITD in the SITD list
+ */
+void
+phcd_iso_sitd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+ td_ptd_map_buff_t *ptd_map_buff;
+ struct ehci_sitd *first_sitd, *next_sitd, *sitd;
+ td_ptd_map_t *td_ptd_map;
+
+ /* Local variable initialization */
+ ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+ first_sitd = (struct ehci_sitd *) urb->hcpriv;
+ sitd = first_sitd;
+
+ /*
+ * Check if there is only one SITD, if so immediately
+ * go and clean it up.
+ */
+ if (sitd->hw_next == EHCI_LIST_END) {
+ if (sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+ td_ptd_map = &ptd_map_buff->map_list[sitd->sitd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ if (status != -ENOMEM) {
+ phci_hcd_mem_free(&sitd->mem_addr);
+ }
+
+ list_del(&sitd->sitd_list);
+ qha_free(qha_cache, sitd);
+
+ urb->hcpriv = 0;
+ return;
+ }
+ /* if(sitd->hw_next == EHCI_LIST_END) */
+ while (1) {
+ /* Get the SITD following the head SITD */
+ next_sitd = (struct ehci_sitd *) (sitd->hw_next);
+ if (next_sitd->hw_next == EHCI_LIST_END) {
+ /*
+ * If the next SITD is the end of the list, check if space have
+ * already been allocated in the PTD array.
+ */
+ if (next_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+ /* Free up its allocation */
+ td_ptd_map =
+ &ptd_map_buff->map_list[next_sitd->
+ sitd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ /*
+ * If the error is not about memory allocation problems, then
+ * free up the memory used.
+ */
+ if (status != -ENOMEM) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_free_list Error]: Memory not available\n");
+ phci_hcd_mem_free(&next_sitd->mem_addr);
+ }
+
+ /* Remove from the SITD list and free up space allocated for SITD structure */
+ list_del(&next_sitd->sitd_list);
+ qha_free(qha_cache, next_sitd);
+ break;
+ }
+
+ /* if(next_itd->hw_next == EHCI_LIST_END) */
+ /*
+ * If SITD is not the end of the list, it only means that it already have everything allocated
+ * and there is no need to check which procedure failed. So just free all resourcs immediately
+ */
+ sitd->hw_next = next_sitd->hw_next;
+
+ td_ptd_map = &ptd_map_buff->map_list[next_sitd->sitd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ phci_hcd_mem_free(&next_sitd->mem_addr);
+ list_del(&next_sitd->sitd_list);
+ qha_free(qha_cache, next_sitd);
+ } /* while(1) */
+
+ /* Now work on the head SITD, it is the last one processed. */
+ if (first_sitd->sitd_index != TD_PTD_INV_PTD_INDEX) {
+ td_ptd_map = &ptd_map_buff->map_list[first_sitd->sitd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ if (status != -ENOMEM) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_free_list Error]: No memory\n");
+ phci_hcd_mem_free(&first_sitd->mem_addr);
+ }
+
+ list_del(&first_sitd->sitd_list);
+ qha_free(qha_cache, first_sitd);
+ urb->hcpriv = 0;
+ return;
+}
+
+/*
+ * phcd_iso_itd_free_list - Free memory used by ITDs in ITD list
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long status
+ * - Variable provided by the calling routine that contain the status of the
+ * ITD list.
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Cleaning up memory used by each ITD in the ITD list
+ */
+void
+phcd_iso_itd_free_list(phci_hcd * hcd, struct urb *urb, unsigned long status)
+{
+ td_ptd_map_buff_t *ptd_map_buff;
+ struct ehci_itd *first_itd, *next_itd, *itd;
+ td_ptd_map_t *td_ptd_map;
+
+ /* Local variable initialization */
+ ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
+ first_itd = (struct ehci_itd *) urb->hcpriv;
+ itd = first_itd;
+
+ /*
+ * Check if there is only one ITD, if so immediately
+ * go and clean it up.
+ */
+ if (itd->hw_next == EHCI_LIST_END) {
+ if (itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+ td_ptd_map = &ptd_map_buff->map_list[itd->itd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ if (status != -ENOMEM) {
+ phci_hcd_mem_free(&itd->mem_addr);
+ }
+
+ list_del(&itd->itd_list);
+ qha_free(qha_cache, itd);
+
+ urb->hcpriv = 0;
+ return;
+ }
+ /* if(itd->hw_next == EHCI_LIST_END) */
+ while (1) {
+ /* Get the ITD following the head ITD */
+ next_itd = (struct ehci_itd *) le32_to_cpu(itd->hw_next);
+ if (next_itd->hw_next == EHCI_LIST_END) {
+ /*
+ * If the next ITD is the end of the list, check if space have
+ * already been allocated in the PTD array.
+ */
+ if (next_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+ /* Free up its allocation */
+ td_ptd_map =
+ &ptd_map_buff->map_list[next_itd->
+ itd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ /*
+ * If the error is not about memory allocation problems, then
+ * free up the memory used.
+ */
+ if (status != -ENOMEM) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_free_list Error]: Memory not available\n");
+ phci_hcd_mem_free(&next_itd->mem_addr);
+ }
+
+ /* Remove from the ITD list and free up space allocated for ITD structure */
+ list_del(&next_itd->itd_list);
+ qha_free(qha_cache, next_itd);
+ break;
+ }
+
+ /* if(next_itd->hw_next == EHCI_LIST_END) */
+ /*
+ * If ITD is not the end of the list, it only means that it already have everything allocated
+ * and there is no need to check which procedure failed. So just free all resourcs immediately
+ */
+ itd->hw_next = next_itd->hw_next;
+
+ td_ptd_map = &ptd_map_buff->map_list[next_itd->itd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ phci_hcd_mem_free(&next_itd->mem_addr);
+ list_del(&next_itd->itd_list);
+ qha_free(qha_cache, next_itd);
+ } /* while(1) */
+
+ /* Now work on the head ITD, it is the last one processed. */
+ if (first_itd->itd_index != TD_PTD_INV_PTD_INDEX) {
+ td_ptd_map = &ptd_map_buff->map_list[first_itd->itd_index];
+ td_ptd_map->state = TD_PTD_NEW;
+ }
+
+ if (status != -ENOMEM) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_iso_itd_free_list Error]: No memory\n");
+ phci_hcd_mem_free(&first_itd->mem_addr);
+ }
+
+ list_del(&first_itd->itd_list);
+ qha_free(qha_cache, first_itd);
+ urb->hcpriv = 0;
+ return;
+} /* phcd_iso_itd_free_list */
+
+void
+phcd_clean_iso_qh(phci_hcd * hcd, struct ehci_qh *qh)
+{
+ unsigned int i = 0;
+ u16 skipmap=0;
+ struct ehci_sitd *sitd;
+ struct ehci_itd *itd;
+
+ iso_dbg(ISO_DBG_ERR, "phcd_clean_iso_qh \n");
+ if (!qh){
+ return;
+ }
+ skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+ skipmap |= qh->periodic_list.ptdlocation;
+ isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+ phci_hcd_mem_free(&qh->memory_addr);
+#endif
+ for (i = 0; i < 16 && qh->periodic_list.ptdlocation; i++) {
+ if (qh->periodic_list.ptdlocation & (0x1 << i)) {
+ printk("[phcd_clean_iso_qh] : %x \n",
+ qh->periodic_list.high_speed);
+
+ qh->periodic_list.ptdlocation &= ~(0x1 << i);
+
+ if (qh->periodic_list.high_speed == 0) {
+ if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd) {
+
+ printk("SITD found \n");
+ sitd = td_ptd_map_buff
+ [TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd;
+#ifndef COMMON_MEMORY
+ phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+ /*
+ if(sitd->urb)
+ urb=sitd->urb;
+ */
+ sitd->urb = NULL;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].state = TD_PTD_NEW;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd = NULL;
+ qha_free(qha_cache, sitd);
+ }
+ } else {
+ if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd) {
+
+ printk("ITD found \n");
+ itd = td_ptd_map_buff
+ [TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd;
+#ifdef COMMON_MEMORY
+ phci_hcd_mem_free(&itd->mem_addr);
+#endif
+
+ /*
+ if(itd->urb)
+ urb=itd->urb;
+ */
+ itd->urb = NULL;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].state = TD_PTD_NEW;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd = NULL;
+ qha_free(qha_cache, itd);
+ }
+ }
+
+ }
+ }
+
+
+}
+
+
+/*
+ * phcd_store_urb_pending - store requested URB into a queue
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long *status
+ * - Variable provided by the calling routine that will contain the status of the
+ * phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Store URB into a queue
+ * - If ther's enough free PTD slots , repairing the PTDs
+ */
+void phcd_clean_periodic_ep(void){
+ periodic_ep[0] = NULL;
+ periodic_ep[1] = NULL;
+}
+
+int
+phcd_clean_urb_pending(phci_hcd * hcd, struct urb *urb)
+{
+ unsigned int i = 0;
+ struct ehci_qh *qhead;
+ struct ehci_sitd *sitd;
+ struct ehci_itd *itd;
+ u16 skipmap=0;;
+
+ iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Enter\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ qhead=urb->hcpriv;
+ if (periodic_ep[0] == qhead->ep) {
+ periodic_ep[0] = NULL;
+
+ }
+
+ if (periodic_ep[1] == qhead->ep) {
+ periodic_ep[1] = NULL;
+ }
+#else
+ qhead = urb->ep->hcpriv;
+ if (periodic_ep[0] == urb->ep) {
+ periodic_ep[0] = NULL;
+
+ }
+
+ if (periodic_ep[1] == urb->ep) {
+ periodic_ep[1] = NULL;
+ }
+#endif
+ if (!qhead) {
+ return 0;
+ }
+ skipmap = isp1763_reg_read16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+ skipmap |= qhead->periodic_list.ptdlocation;
+ isp1763_reg_write16(hcd->dev, hcd->regs.isotdskipmap, skipmap);
+#ifdef COMMON_MEMORY
+ phci_hcd_mem_free(&qhead->memory_addr);
+#endif
+
+ for (i = 0; i < 16 && qhead->periodic_list.ptdlocation; i++) {
+
+ qhead->periodic_list.ptdlocation &= ~(0x1 << i);
+
+ if (qhead->periodic_list.ptdlocation & (0x1 << i)) {
+
+ printk("[phcd_clean_urb_pending] : %x \n",
+ qhead->periodic_list.high_speed);
+
+ if (qhead->periodic_list.high_speed == 0) {
+
+ if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd) {
+
+ sitd = td_ptd_map_buff
+ [TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd;
+#ifndef COMMON_MEMORY
+ phci_hcd_mem_free(&sitd->mem_addr);
+#endif
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].state = TD_PTD_NEW;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].sitd = NULL;
+ qha_free(qha_cache, sitd);
+ }
+ } else {
+
+ if (td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd) {
+
+ itd = td_ptd_map_buff
+ [TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd;
+#ifdef COMMON_MEMORY
+ phci_hcd_mem_free(&itd->mem_addr);
+#endif
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].state = TD_PTD_NEW;
+ td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL].
+ map_list[i].itd = NULL;
+ qha_free(qha_cache, itd);
+ }
+ }
+
+ }
+
+ }
+ INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+ iso_dbg(ISO_DBG_ENTRY, "[phcd_clean_urb_pending] : Exit\n");
+ return 0;
+}
+
+
+
+int
+phcd_store_urb_pending(phci_hcd * hcd, int index, struct urb *urb, int *status)
+{
+ unsigned int uiNumofPTDs = 0;
+ unsigned int uiNumofSlots = 0;
+ unsigned int uiMult = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Enter\n");
+ if (urb != NULL) {
+ if (periodic_ep[0] != urb->ep && periodic_ep[1] != urb->ep) {
+ if (periodic_ep[0] == NULL) {
+ // printk("storing in 0 %x %x\n",urb,urb->pipe);
+ periodic_ep[0] = urb->ep;
+ } else if (periodic_ep[1] == NULL) {
+ printk("storing in 1\n");
+ periodic_ep[1] = urb->ep;
+ usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+ return -1;
+ } else {
+ iso_dbg(ISO_DBG_ERR,
+ "Support only 2 ISO endpoints simultaneously \n");
+ *status = -1;
+ return -1;
+ }
+ }
+ usb_hcd_link_urb_to_ep(&(hcd->usb_hcd), urb);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : Add an urb into gstUrb_pending array at index : %d\n",
+ giUrbCount);
+ giUrbCount++;
+ } else {
+
+ iso_dbg(ISO_DBG_ENTRY,
+ "[phcd_store_urb_pending] : getting urb from list \n");
+ if (index > 0 && index < 2) {
+ if (periodic_ep[index - 1]){
+ urb = container_of(periodic_ep[index - 1]->
+ urb_list.next, struct urb,
+ urb_list);
+ }
+ } else {
+ iso_dbg(ISO_DBG_ERR, " Unknown enpoints Error \n");
+ *status = -1;
+ return -1;
+ }
+
+ }
+
+
+ if ((urb != NULL && (urb->ep->urb_list.next == &urb->urb_list))){
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : periodic_sched : %d\n",
+ hcd->periodic_sched);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : number_of_packets : %d\n",
+ urb->number_of_packets);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : Maximum PacketSize : %d\n",
+ usb_maxpacket(urb->dev,urb->pipe, usb_pipeout(urb->pipe)));
+ /*if enough free slots */
+ if (urb->dev->speed == USB_SPEED_FULL) { /*for FULL SPEED */
+ // if (hcd->periodic_sched <
+ // MAX_PERIODIC_SIZE - urb->number_of_packets) {
+ if(1){
+ if (phcd_submit_iso(hcd,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ struct usb_host_endpoint *ep,
+ #endif
+ urb,
+ ( unsigned long *) &status) == 0) {
+ pehci_hcd_iso_schedule(hcd, urb);
+ } else{
+ //*status = 0;
+ }
+ }
+ } else if (urb->dev->speed == USB_SPEED_HIGH) { /*for HIGH SPEED */
+ /*number of slots for 1 PTD */
+ uiNumofSlots = NUMMICROFRAME / urb->interval;
+ /*max packets size */
+ uiMult = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ /*mult */
+ uiMult = 1 + ((uiMult >> 11) & 0x3);
+ /*number of PTDs need to schedule for this PTD */
+ uiNumofPTDs =
+ (urb->number_of_packets / uiMult) /
+ uiNumofSlots;
+ if ((urb->number_of_packets / uiMult) % uiNumofSlots != 0){
+ uiNumofPTDs += 1;
+ }
+
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : interval : %d\n",
+ urb->interval);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : uiMult : %d\n",
+ uiMult);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : uiNumofPTDs : %d\n",
+ uiNumofPTDs);
+
+ if (hcd->periodic_sched <=
+ MAX_PERIODIC_SIZE - uiNumofPTDs) {
+
+ if (phcd_submit_iso(hcd,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ struct usb_host_endpoint *ep,
+ #endif
+ urb, (unsigned long *) &status)== 0) {
+
+ pehci_hcd_iso_schedule(hcd, urb);
+ }
+ } else{
+ *status = 0;
+ }
+ }
+ } else{
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_store_urb_pending] : nextUrb is NULL\n");
+ }
+#endif
+ iso_dbg(ISO_DBG_ENTRY, "[phcd_store_urb_pending] : Exit\n");
+ return 0;
+}
+
+/*
+ * phcd_submit_iso - ISO transfer URB submit routine
+ *
+ * phci_hcd *hcd
+ * - Main host controller driver structure
+ * struct urb *urb
+ * - USB Request Block, contains information regarding the type and how much data
+ * is requested to be transferred.
+ * unsigned long *status
+ * - Variable provided by the calling routine that will contain the status of the
+ * phcd_submit_iso actions
+ *
+ * API Description
+ * This is mainly responsible for:
+ * - Allocating memory for the endpoint information structure (pQHead_st)
+ * - Requesting for bus bandwidth from the USB core
+ * - Allocating and initializing Payload and PTD memory
+ */
+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)
+{
+ struct _periodic_list *periodic_list;
+ struct hcd_dev *dev;
+ struct ehci_qh *qhead;
+ struct ehci_itd *itd, *prev_itd;
+ struct ehci_sitd *sitd, *prev_sitd;
+ struct list_head *sitd_itd_list;
+ unsigned long ep_in, max_pkt, mult;
+ unsigned long bus_time, high_speed, start_frame;
+ unsigned long temp;
+ unsigned long packets;
+ /*for high speed device */
+ unsigned int iMicroIndex = 0;
+ unsigned int iNumofSlots = 0;
+ unsigned int iNumofPTDs = 0;
+ unsigned int iPTDIndex = 0;
+ unsigned int iNumofPks = 0;
+ int iPG = 0;
+ dma_addr_t buff_dma;
+ unsigned long length, offset;
+ int i = 0;
+
+ iso_dbg(ISO_DBG_ENTRY, "phcd_submit_iso Entry\n");
+
+ *status = 0;
+ /* Local variable initialization */
+ high_speed = 0;
+ periodic_list = &hcd->periodic_list[0];
+ dev = (struct hcd_dev *) urb->hcpriv;
+ urb->hcpriv = (void *) 0;
+ prev_itd = (struct ehci_itd *) 0;
+ itd = (struct ehci_itd *) 0;
+ prev_sitd = (struct ehci_sitd *) 0;
+ sitd = (struct ehci_sitd *) 0;
+ start_frame = 0;
+
+ ep_in = usb_pipein(urb->pipe);
+
+ /*
+ * Take the endpoint, if there is still no memory allocated
+ * for it allocate some and indicate this is for ISO.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ qhead = ep->hcpriv;
+#else
+ qhead = urb->ep->hcpriv;
+#endif
+ if (!qhead) {
+
+ qhead = phci_hcd_qh_alloc(hcd);
+ if (qhead == 0) {
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: Not enough memory\n");
+ return -ENOMEM;
+ }
+
+ qhead->type = TD_PTD_BUFF_TYPE_ISTL;
+ INIT_LIST_HEAD(&qhead->periodic_list.sitd_itd_head);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ qhead->ep=ep;
+ ep->hcpriv = qhead;
+ urb->hcpriv=qhead;
+#else
+ urb->ep->hcpriv = qhead;
+#endif
+ }
+
+ urb->hcpriv=qhead;
+
+ /* if(!qhead) */
+ /*
+ * Get the number of additional packets that the endpoint can support during a
+ * single microframe.
+ */
+ max_pkt = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ /*
+ * We need to add 1 since our Multi starts with 1 instead of the USB specs defined
+ * zero (0).
+ */
+ mult = 1 + ((max_pkt >> 11) & 0x3);
+
+ /* This is the actual length per for the whole transaction */
+ max_pkt *= mult;
+
+ /* Check bandwidth */
+ bus_time = 0;
+
+ if (urb->dev->speed == USB_SPEED_FULL) {
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ if (urb->bandwidth == 0) {
+ bus_time = usb_check_bandwidth(urb->dev, urb);
+ if (bus_time < 0) {
+ usb_dec_dev_use(urb->dev);
+ *status = bus_time;
+ return *status;
+ }
+ }
+#else
+#endif
+ } else { /*HIGH SPEED */
+
+ high_speed = 1;
+
+ /*
+ * Calculate bustime as dictated by the USB Specs Section 5.11.3
+ * for high speed ISO
+ */
+ bus_time = 633232L;
+ bus_time +=
+ (2083L * ((3167L + BitTime(max_pkt) * 1000L) / 1000L));
+ bus_time = bus_time / 1000L;
+ bus_time += BW_HOST_DELAY;
+ bus_time = NS_TO_US(bus_time);
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ usb_claim_bandwidth(urb->dev, urb, bus_time, 1);
+#else
+#endif
+
+ qhead->periodic_list.ptdlocation = 0;
+ /* Initialize the start split (ssplit) and complete split (csplit) variables of qhead */
+ if (phcd_iso_scheduling_info(hcd, qhead, max_pkt, high_speed, ep_in) <
+ 0) {
+
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: No space available\n");
+ return -ENOSPC;
+ }
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ iNumofSlots = NUMMICROFRAME / urb->interval;
+ /*number of PTDs need to schedule for this PTD */
+ iNumofPTDs = (urb->number_of_packets / mult) / iNumofSlots;
+ if ((urb->number_of_packets / mult) % iNumofSlots != 0){
+ /*get remainder */
+ iNumofPTDs += 1;
+ }
+ }
+ if (urb->iso_frame_desc[0].offset != 0) {
+ *status = -EINVAL;
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: Invalid value\n");
+ return *status;
+ }
+ if (1) {
+ /* Calculate the current frame number */
+ if (0){
+ if (urb->transfer_flags & URB_ISO_ASAP){
+ start_frame =
+ isp1763_reg_read16(hcd->dev,
+ hcd->regs.frameindex,
+ start_frame);
+ } else {
+ start_frame = urb->start_frame;
+ }
+ }
+
+ start_frame =
+ isp1763_reg_read16(hcd->dev, hcd->regs.frameindex,
+ start_frame);
+
+ /* The only valid bits of the frame index is the lower 14 bits. */
+
+ /*
+ * Remove the count for the micro frame (uSOF) and just leave the
+ * count for the frame (SOF). Since 1 SOF is equal to 8 uSOF then
+ * shift right by three is like dividing it by 8 (each shift is divide by two)
+ */
+ start_frame >>= 3;
+ if (urb->dev->speed != USB_SPEED_HIGH){
+ start_frame += 1;
+ }else{
+ start_frame += 2;
+ }
+ start_frame = start_frame & PTD_FRAME_MASK;
+ temp = start_frame;
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ qhead->next_uframe =
+ start_frame + urb->number_of_packets;
+ } else {
+ qhead->next_uframe = start_frame + iNumofPTDs;
+ }
+ qhead->next_uframe %= PTD_FRAME_MASK;
+ iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: startframe = %ld\n",
+ start_frame);
+ } else {
+ /*
+ * The periodic frame list size is only 32 elements deep, so we need
+ * the frame index to be less than or equal to 32 (actually 31 if we
+ * start from 0)
+ */
+ start_frame = (qhead->next_uframe) % PTD_FRAME_MASK;
+ if (urb->dev->speed != USB_SPEED_HIGH){
+ qhead->next_uframe =
+ start_frame + urb->number_of_packets;
+ iNumofPTDs=urb->number_of_packets;
+ } else {
+ qhead->next_uframe = start_frame + iNumofPTDs;
+ }
+
+ qhead->next_uframe %= PTD_FRAME_MASK;
+ }
+
+
+ iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Start frame index: %ld\n",
+ start_frame);
+ iso_dbg(ISO_DBG_DATA, "[phcd_submit_iso]: Max packet: %d\n",
+ (int) max_pkt);
+
+#ifdef COMMON_MEMORY
+ if(urb->number_of_packets>8 && urb->dev->speed!=USB_SPEED_HIGH)
+ phci_hcd_mem_alloc(8*max_pkt, &qhead->memory_addr, 0);
+ else
+ phci_hcd_mem_alloc(urb->transfer_buffer_length, &qhead->memory_addr, 0);
+ if (urb->transfer_buffer_length && ((qhead->memory_addr.phy_addr == 0)
+ || (qhead->memory_addr.virt_addr ==0))) {
+ iso_dbg(ISO_DBG_ERR,
+ "[URB FILL MEMORY Error]: No payload memory available\n");
+ return -ENOMEM;
+ }
+#endif
+
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ iNumofPks = urb->number_of_packets;
+ qhead->totalptds=urb->number_of_packets;
+ qhead->actualptds=0;
+
+ /* Make as many tds as number of packets */
+ for (packets = 0; packets < urb->number_of_packets; packets++) {
+ /*
+ * Allocate memory for the SITD data structure and initialize it.
+ *
+ * This data structure follows the format of the SITD
+ * structure defined by the EHCI standard on the top part
+ * but also contains specific elements in the bottom
+ * part
+ */
+ sitd = kmalloc(sizeof(*sitd), GFP_ATOMIC);
+ if (!sitd) {
+ *status = -ENOMEM;
+ if (((int)(qhead->next_uframe -
+ urb->number_of_packets)) < 0){
+ /*plus max PTDs*/
+ qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;
+
+ }
+ qhead->next_uframe -= urb->number_of_packets;
+
+ /* Handle SITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_sitd_free_list(hcd, urb,
+ *status);
+ }
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: No memory available\n");
+ return *status;
+ }
+
+ memset(sitd, 0, sizeof(struct ehci_sitd));
+
+ INIT_LIST_HEAD(&sitd->sitd_list);
+
+ sitd->sitd_dma = (u32) (sitd);
+ sitd->urb = urb;
+
+ /*
+ * Indicate that this SITD is the last in the list.
+ *
+ * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+ * (0xFFFFFFFF). This would indicate when we allocate
+ * a PTD that this SITD did not have a PTD allocated
+ * before.
+ */
+
+ sitd->hw_next = EHCI_LIST_END;
+ sitd->sitd_index = TD_PTD_INV_PTD_INDEX;
+
+ /* This SITD will go into this frame */
+ sitd->framenumber = start_frame + packets;
+ sitd->start_frame = temp + packets;
+
+ /* Number of the packet */
+ sitd->index = packets;
+
+ sitd->framenumber = sitd->framenumber & PTD_FRAME_MASK;
+ sitd->ssplit = qhead->ssplit;
+ sitd->csplit = qhead->csplit;
+
+ /* Initialize the following elements of the ITS structure
+ * > sitd->length = length; -- the size of the request
+ * > sitd->multi = multi; -- the number of transactions for
+ * this EP per micro frame
+ * > sitd->hw_bufp[0] = buf_dma; -- The base address of the buffer where
+ * to put the data (this base address was
+ * the buffer provided plus the offset)
+ * And then, allocating memory from the PAYLOAD memory area, where the data
+ * coming from the requesting party will be placed or data requested by the
+ * requesting party will be retrieved when it is available.
+ */
+ *status = phcd_iso_sitd_fill(hcd, sitd, urb, packets);
+
+ if (*status != 0) {
+ if (((int)(qhead->next_uframe -
+ urb->number_of_packets)) < 0){
+ /*plus max PTDs*/
+ qhead->next_uframe = qhead->next_uframe +
+ PTD_PERIODIC_SIZE;
+ }
+ qhead->next_uframe -= urb->number_of_packets;
+
+ /* Handle SITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_sitd_free_list(hcd, urb,
+ *status);
+ }
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: Error in filling up SITD\n");
+ return *status;
+ }
+
+ /*
+ * If this SITD is not the head/root SITD, link this SITD to the SITD
+ * that came before it.
+ */
+ if (prev_sitd) {
+ prev_sitd->hw_next = (u32) (sitd);
+ }
+
+ prev_sitd = sitd;
+
+ if(packets<8){ //bcs of memory constraint , we use only first 8 PTDs if number_of_packets is more than 8.
+ /*
+ * Allocate an ISO PTD from the ISO PTD map list and
+ * set the equivalent bit of the allocated PTD to active
+ * in the bitmap so that this PTD will be included into
+ * the periodic schedule
+ */
+ phcd_iso_get_sitd_ptd_index(hcd, sitd);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_submit_iso]: SITD index %d\n",
+ sitd->sitd_index);
+
+ /*if we dont have any space left */
+ if (sitd->sitd_index == TD_PTD_INV_PTD_INDEX) {
+ *status = -ENOSPC;
+ if (((int) (qhead->next_uframe -
+ urb->number_of_packets)) < 0){
+ /*plus max PTDs*/
+ qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;
+ }
+ qhead->next_uframe -= urb->number_of_packets;
+
+ /* Handle SITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_sitd_free_list(hcd, urb,
+ *status);
+ }
+ return *status;
+ }
+ qhead->actualptds++;
+ }
+ /* Insert this td into the periodic list */
+
+ sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+ list_add_tail(&sitd->sitd_list, sitd_itd_list);
+ qhead->periodic_list.high_speed = 0;
+ if(sitd->sitd_index!=TD_PTD_INV_PTD_INDEX)
+ qhead->periodic_list.ptdlocation |=
+ 0x1 << sitd->sitd_index;
+ /* Inidcate that a new SITD have been scheduled */
+ hcd->periodic_sched++;
+
+ /* Determine if there are any SITD scheduled before this one. */
+ if (urb->hcpriv == 0){
+ urb->hcpriv = sitd;
+ }
+ } /* for(packets = 0; packets... */
+ } else if (urb->dev->speed == USB_SPEED_HIGH) {
+ iNumofPks = iNumofPTDs;
+
+ packets = 0;
+ iPTDIndex = 0;
+ while (packets < urb->number_of_packets) {
+ iNumofSlots = NUMMICROFRAME / urb->interval;
+ /*
+ * Allocate memory for the ITD data structure and initialize it.
+ *
+ * This data structure follows the format of the ITD
+ * structure defined by the EHCI standard on the top part
+ * but also contains specific elements in the bottom
+ * part
+ */
+ itd = kmalloc(sizeof(*itd), GFP_ATOMIC);
+ if (!itd) {
+ *status = -ENOMEM;
+ if(((int) (qhead->next_uframe - iNumofPTDs))<0){
+ /*plus max PTDs*/
+ qhead->next_uframe = qhead->next_uframe +
+ PTD_PERIODIC_SIZE;
+ }
+ qhead->next_uframe -= iNumofPTDs;
+
+ /* Handle ITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_itd_free_list(hcd, urb,
+ *status);
+ }
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: No memory available\n");
+ return *status;
+ }
+ memset(itd, 0, sizeof(struct ehci_itd));
+
+ INIT_LIST_HEAD(&itd->itd_list);
+
+ itd->itd_dma = (u32) (itd);
+ itd->urb = urb;
+ /*
+ * Indicate that this ITD is the last in the list.
+ *
+ * Also set the itd_index to TD_PTD_INV_PTD_INDEX
+ * (0xFFFFFFFF). This would indicate when we allocate
+ * a PTD that this SITD did not have a PTD allocated
+ * before.
+ */
+
+ itd->hw_next = EHCI_LIST_END;
+ itd->itd_index = TD_PTD_INV_PTD_INDEX;
+ /* This ITD will go into this frame */
+ itd->framenumber = start_frame + iPTDIndex;
+ /* Number of the packet */
+ itd->index = packets;
+
+ itd->framenumber = itd->framenumber & 0x1F;
+
+ itd->ssplit = qhead->ssplit;
+ itd->csplit = qhead->csplit;
+
+ /*caculate the number of packets for this itd */
+ itd->num_of_pkts = iNumofSlots * mult;
+ /*for the case , urb number_of_packets is less than (number of slot*mult*x times) */
+ if (itd->num_of_pkts >= urb->number_of_packets)
+ {
+ itd->num_of_pkts = urb->number_of_packets;
+ }
+ else {
+ if (itd->num_of_pkts >
+ urb->number_of_packets - packets){
+ itd->num_of_pkts =
+ urb->number_of_packets -
+ packets;
+ }
+ }
+
+ /* Initialize the following elements of the ITS structure
+ * > itd->length = length; -- the size of the request
+ * > itd->multi = multi; -- the number of transactions for
+ * this EP per micro frame
+ * > itd->hw_bufp[0] = buf_dma; -- The base address of the buffer where
+ * to put the data (this base address was
+ * the buffer provided plus the offset)
+ * And then, allocating memory from the PAYLOAD memory area, where the data
+ * coming from the requesting party will be placed or data requested by the
+ * requesting party will be retrieved when it is available.
+ */
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_submit_iso] packets index = %ld itd->num_of_pkts = %d\n",
+ packets, itd->num_of_pkts);
+ *status =
+ phcd_iso_itd_fill(hcd, itd, urb, packets,
+ itd->num_of_pkts);
+ if (*status != 0) {
+ if (((int) (qhead->next_uframe - iNumofPTDs)) <
+ 0) {
+ qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE; /*plus max PTDs*/
+ }
+ qhead->next_uframe -= iNumofPTDs;
+
+ /* Handle SITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_itd_free_list(hcd, urb,
+ *status);
+ }
+ iso_dbg(ISO_DBG_ERR,
+ "[phcd_submit_iso Error]: Error in filling up ITD\n");
+ return *status;
+ }
+
+ iPG = 0;
+ iMicroIndex = 0;
+ while (iNumofSlots > 0) {
+ offset = urb->iso_frame_desc[packets].offset;
+ /* Buffer for this packet */
+ buff_dma =
+ (u32) ((unsigned char *) urb->
+ transfer_buffer + offset);
+
+ /*for the case mult is 2 or 3 */
+ length = 0;
+ for (i = packets; i < packets + mult; i++) {
+ length += urb->iso_frame_desc[i].length;
+ }
+ itd->hw_transaction[iMicroIndex] =
+ EHCI_ISOC_ACTIVE | (length &
+ EHCI_ITD_TRANLENGTH)
+ << 16 | iPG << 12 | buff_dma;
+
+ if (itd->hw_bufp[iPG] != buff_dma){
+ itd->hw_bufp[++iPG] = buff_dma;
+ }
+
+ iso_dbg(ISO_DBG_DATA,
+ "[%s] offset : %ld buff_dma : 0x%08x length : %ld\n",
+ __FUNCTION__, offset,
+ (unsigned int) buff_dma, length);
+
+ itd->ssplit |= 1 << iMicroIndex;
+ packets++;
+ iMicroIndex += urb->interval;
+ iNumofSlots--;
+
+ /*last packets or last slot */
+ if (packets == urb->number_of_packets
+ || iNumofSlots == 0) {
+
+ itd->hw_transaction[iMicroIndex] |=
+ EHCI_ITD_IOC;
+
+ break;
+
+ }
+ }
+
+ /*
+ * If this SITD is not the head/root SITD, link this SITD to the SITD
+ * that came before it.
+ */
+ if (prev_itd) {
+ prev_itd->hw_next = (u32) (itd);
+ }
+
+ prev_itd = itd;
+
+ /*
+ * Allocate an ISO PTD from the ISO PTD map list and
+ * set the equivalent bit of the allocated PTD to active
+ * in the bitmap so that this PTD will be included into
+ * the periodic schedule
+ */
+
+
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_submit_iso]: ITD index %d\n",
+ itd->framenumber);
+ phcd_iso_get_itd_ptd_index(hcd, itd);
+ iso_dbg(ISO_DBG_DATA,
+ "[phcd_submit_iso]: ITD index %d\n",
+ itd->itd_index);
+
+ /*if we dont have any space left */
+ if (itd->itd_index == TD_PTD_INV_PTD_INDEX) {
+ *status = -ENOSPC;
+ if (((int) (qhead->next_uframe - iNumofPTDs)) <
+ 0){
+ /*plus max PTDs*/
+ qhead->next_uframe = qhead->next_uframe + PTD_PERIODIC_SIZE;
+ }
+ qhead->next_uframe -= iNumofPTDs;
+
+ /* Handle SITD list cleanup */
+ if (urb->hcpriv) {
+ phcd_iso_itd_free_list(hcd, urb,
+ *status);
+ }
+ return *status;
+ }
+
+ sitd_itd_list = &qhead->periodic_list.sitd_itd_head;
+ list_add_tail(&itd->itd_list, sitd_itd_list);
+ qhead->periodic_list.high_speed = 1;
+ qhead->periodic_list.ptdlocation |=
+ 0x1 << itd->itd_index;
+
+ /* Inidcate that a new SITD have been scheduled */
+ hcd->periodic_sched++;
+
+ /* Determine if there are any ITD scheduled before this one. */
+ if (urb->hcpriv == 0){
+ urb->hcpriv = itd;
+ }
+ iPTDIndex++;
+
+ } /*end of while */
+ }
+
+ /*end of HIGH SPEED */
+ /* Last td of current transaction */
+ if (high_speed == 0){
+ sitd->hw_next = EHCI_LIST_END;
+ }
+ urb->error_count = 0;
+ return *status;
+} /* phcd_submit_iso */
+#endif /* CONFIG_ISO_SUPPORT */
diff --git a/drivers/usb/host/isp1763-mem.c b/drivers/usb/host/isp1763-mem.c new file mode 100644 index 00000000000..dfcaef901ba --- /dev/null +++ b/drivers/usb/host/isp1763-mem.c @@ -0,0 +1,355 @@ +/*
+* 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.
+*
+* This is a host controller driver file. Memory initialization, allocation, and
+* deallocation are handled here.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifdef CONFIG_ISO_SUPPORT
+
+/*memory utilization fuctions*/
+void
+phci_hcd_mem_init(void)
+{
+ int i = 0;
+ u32 start_addr = 0x1000;
+ struct isp1763_mem_addr *memaddr;
+ for (i = 0; i < BLK_TOTAL; i++) {
+ memaddr = &memalloc[i];
+ memset(memaddr, 0, sizeof *memaddr);
+ }
+ /*initialize block of 128bytes */
+ for (i = 0; i < BLK_128_; i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_128;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_128;
+ }
+ /*initialize block of 256bytes */
+ for (i = BLK_128_; i < BLK_256_; i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_256;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_256;
+ }
+ /*initialize block of 1024bytes */
+ for (i = BLK_128_ + BLK_256_; i < (BLK_128_ + BLK_256_ + BLK_1024_);
+ i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_1024;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_1024;
+ }
+
+ /*initialize block of 2kbytes */
+ for (i = (BLK_128_ + BLK_256_ + BLK_1024_);
+ i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_2048;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_2048;
+ }
+ /* initialize block of 4kbytes */
+ for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_);
+ i < (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_);
+ i++){
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_4096;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_4096;
+ }
+ /* initialize block of 8kbytes */
+ for (i = (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_); i <
+ (BLK_128_ + BLK_256_ + BLK_1024_ + BLK_2048_ + BLK_4096_ +
+ BLK_8196_); i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_8192;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_8192;
+ }
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+ /*block number to be freed */
+ int block = memptr->blk_num;
+
+ if (block < BLK_TOTAL){
+ if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+ memalloc[block].used = 0;
+ memptr->used = 0;
+ }
+ }
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+ u32 blk_size = size;
+ u16 i;
+ u32 nextblk1 = 0, nextblk4 = 0;
+ u32 start = 0, end = 0;
+ struct isp1763_mem_addr *memaddr = 0;
+
+ memset(memptr, 0, sizeof *memptr);
+
+ if (blk_size == 0) {
+ memptr->phy_addr = 0;
+ memptr->virt_addr = 0;
+ memptr->blk_size = 0;
+ memptr->num_alloc = 0;
+ memptr->blk_num = 0;
+ return;
+ }
+
+ for (i = 0; i < BLK_TOTAL; i++) {
+ memaddr = &memalloc[i];
+ if (!memaddr->used && size <= memaddr->blk_size) {
+ memaddr->used = 1;
+ memptr->used = 1;
+ memptr->blk_num = i;
+ memptr->blk_size = memaddr->blk_size;
+ memptr->phy_addr = memaddr->phy_addr;
+ memptr->virt_addr = memptr->phy_addr;
+ return;
+ }
+ }
+
+ printk("isp1763-mem: No suitable block found!");
+
+ return;
+ /*end of the 1k blocks */
+ nextblk1 = BLK_256_ + BLK_1024_;
+ /*end of the 4k blocks */
+ nextblk4 = nextblk1 + BLK_4096_;
+
+ if (blk_size <= BLK_SIZE_128) {
+ blk_size = BLK_SIZE_128;
+ start = 0;
+ end = BLK_256_;
+ }
+ if (blk_size <= BLK_SIZE_256) {
+ blk_size = BLK_SIZE_256;
+ start = 0;
+ end = BLK_256_;
+ } else if (blk_size <= BLK_SIZE_1024) {
+ blk_size = BLK_SIZE_1024;
+ start = BLK_256_;
+ end = start + BLK_1024_;
+ } else if (blk_size > BLK_SIZE_1024) {
+ blk_size = BLK_SIZE_4096;
+ start = BLK_256_ + BLK_1024_;
+ end = start + BLK_4096_;
+ }
+
+ for (i = start; i < end; i++) {
+ memaddr = &memalloc[i];
+ if (!memaddr->used) {
+ memaddr->used = 1;
+ memptr->blk_num = i;
+ memptr->used = 1;
+ memptr->blk_size = blk_size;
+ memptr->phy_addr = memaddr->phy_addr;
+ memptr->virt_addr = memptr->phy_addr;
+ return;
+ }
+ }
+
+ /*look for in the next block if memory is free */
+ /*start from the first place of the next block */
+ start = end;
+
+ /*for 1k and 256 size request only 4k can be returned */
+ end = nextblk4;
+
+ for (i = start; i < end; i++) {
+ memaddr = &memalloc[i];
+ if (!memaddr->used) {
+ memaddr->used = 1;
+ memptr->used = 1;
+ memptr->blk_num = i;
+ memptr->blk_size = blk_size;
+ memptr->phy_addr = memaddr->phy_addr;
+ memptr->virt_addr = memptr->phy_addr;
+ return;
+ }
+ }
+
+}
+
+#else
+
+void
+phci_hcd_mem_init(void)
+{
+ int i = 0;
+ u32 start_addr = 0x1000;
+ struct isp1763_mem_addr *memaddr;
+ for (i = 0; i < BLK_TOTAL; i++) {
+ memaddr = &memalloc[i];
+ memset(memaddr, 0, sizeof *memaddr);
+ }
+
+ /*initialize block of 256bytes */
+ for (i = 0; i < BLK_256_; i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_256;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_256;
+ }
+ /*initialize block of 1024bytes */
+ for (i = BLK_256_; i < (BLK_256_ + BLK_1024_); i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_1024;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_1024;
+ }
+
+ /*initialize block of 4kbytes */
+ for (i = (BLK_256_ + BLK_1024_); i < (BLK_256_ + BLK_1024_ + BLK_4096_);
+ i++) {
+ memaddr = &memalloc[i];
+ memaddr->blk_num = i;
+ memaddr->used = 0;
+ memaddr->blk_size = BLK_SIZE_4096;
+ memaddr->phy_addr = start_addr;
+ start_addr += BLK_SIZE_4096;
+ }
+
+}
+
+
+/*free memory*/
+static void
+phci_hcd_mem_free(struct isp1763_mem_addr *memptr)
+{
+ /*block number to be freed */
+ int block = memptr->blk_num;
+
+ if (block < BLK_TOTAL)
+ if ((memptr->blk_size) && (memalloc[block].used != 0)) {
+ memalloc[block].used = 0;
+ memptr->used = 0;
+ }
+}
+
+
+/*allocate memory*/
+static void
+phci_hcd_mem_alloc(u32 size, struct isp1763_mem_addr *memptr, u32 flag)
+{
+ u32 blk_size = size;
+ u16 i;
+ u32 nextblk1 = 0, nextblk4 = 0;
+ u32 start = 0, end = 0;
+ struct isp1763_mem_addr *memaddr = 0;
+
+ memset(memptr, 0, sizeof *memptr);
+
+ pehci_print("phci_hcd_mem_alloc(size = %d)\n", size);
+
+ if (blk_size == 0) {
+ memptr->phy_addr = 0;
+ memptr->virt_addr = 0;
+ memptr->blk_size = 0;
+ memptr->num_alloc = 0;
+ memptr->blk_num = 0;
+ return;
+ }
+
+ /*end of the 1k blocks */
+ nextblk1 = BLK_256_ + BLK_1024_;
+ /*end of the 4k blocks */
+ nextblk4 = nextblk1 + BLK_4096_;
+
+
+ if (blk_size <= BLK_SIZE_256) {
+ blk_size = BLK_SIZE_256;
+ start = 0;
+ end = BLK_256_;
+ } else if (blk_size <= BLK_SIZE_1024) {
+ blk_size = BLK_SIZE_1024;
+ start = BLK_256_;
+ end = start + BLK_1024_;
+ } else if (blk_size > BLK_SIZE_1024) {
+ blk_size = BLK_SIZE_4096;
+ start = BLK_256_ + BLK_1024_;
+ end = start + BLK_4096_;
+ }
+
+ for (i = start; i < end; i++) {
+ memaddr = &memalloc[i];
+ if (!memaddr->used) {
+ memaddr->used = 1;
+ memptr->blk_num = i;
+ memptr->used = 1;
+ memptr->blk_size = blk_size;
+ memptr->phy_addr = memaddr->phy_addr;
+ memptr->virt_addr = memptr->phy_addr;
+ return;
+ }
+ }
+
+ /*look for in the next block if memory is free */
+ /*start from the first place of the next block */
+ start = end;
+
+ /*for 1k and 256 size request only 4k can be returned */
+ end = nextblk4;
+
+ for (i = start; i < end; i++) {
+ memaddr = &memalloc[i];
+ if (!memaddr->used) {
+ memaddr->used = 1;
+ memptr->used = 1;
+ memptr->blk_num = i;
+ memptr->blk_size = blk_size;
+ memptr->phy_addr = memaddr->phy_addr;
+ memptr->virt_addr = memptr->phy_addr;
+ return;
+ }
+ }
+
+}
+
+#endif
diff --git a/drivers/usb/host/isp1763-otg.c b/drivers/usb/host/isp1763-otg.c new file mode 100644 index 00000000000..a951c338124 --- /dev/null +++ b/drivers/usb/host/isp1763-otg.c @@ -0,0 +1,189 @@ +/*
+* 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.
+*
+* This is a host controller driver file. OTG related events are handled here.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/*hub device which connected with root port*/
+struct usb_device *hubdev = 0;
+/* hub interrupt urb*/
+struct urb *huburb;
+
+/*return otghub from here*/
+struct usb_device *
+phci_register_otg_device(struct isp1763_dev *dev)
+{
+ printk("OTG dev %x %d\n",(u32) hubdev, hubdev->devnum);
+ if (hubdev && hubdev->devnum >= 0x2) {
+ return hubdev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(phci_register_otg_device);
+
+/*suspend the otg port(0)
+ * needed when port is switching
+ * from host to device
+ * */
+int
+phci_suspend_otg_port(struct isp1763_dev *dev, u32 command)
+{
+ int status = 0;
+ hubdev->otgstate = USB_OTG_SUSPEND;
+ if (huburb->status == -EINPROGRESS) {
+ huburb->status = 0;
+ }
+
+ huburb->status = 0;
+ huburb->complete(huburb);
+ return status;
+}
+EXPORT_SYMBOL(phci_suspend_otg_port);
+
+/*set the flag to enumerate the device*/
+int
+phci_enumerate_otg_port(struct isp1763_dev *dev, u32 command)
+{
+ /*set the flag to enumerate */
+ /*connect change interrupt will happen from
+ * phci_intl_worker only
+ * */
+ hubdev->otgstate = USB_OTG_ENUMERATE;
+ if (huburb->status == -EINPROGRESS) {
+ huburb->status = 0;
+ }
+ /*complete the urb */
+
+ huburb->complete(huburb);
+
+ /*reset the otghub urb status */
+ huburb->status = -EINPROGRESS;
+ return 0;
+}
+EXPORT_SYMBOL(phci_enumerate_otg_port);
+
+/*host controller resume sequence at otg port*/
+int
+phci_resume_otg_port(struct isp1763_dev *dev, u32 command)
+{
+ printk("Resume is called\n");
+ hubdev->otgstate = USB_OTG_RESUME;
+ if (huburb->status == -EINPROGRESS) {
+ huburb->status = 0;
+ }
+ /*complete the urb */
+
+ huburb->complete(huburb);
+
+ /*reset the otghub urb status */
+ huburb->status = -EINPROGRESS;
+ return 0;
+}
+EXPORT_SYMBOL(phci_resume_otg_port);
+/*host controller remote wakeup sequence at otg port*/
+int
+phci_remotewakeup(struct isp1763_dev *dev)
+{
+ printk("phci_remotewakeup_otg_port is called\n");
+ hubdev->otgstate = USB_OTG_REMOTEWAKEUP;
+ if(huburb->status == -EINPROGRESS)
+ huburb->status = 0;
+ /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+ huburb->complete(huburb,NULL);
+#else
+ huburb->complete(huburb);
+#endif
+ /*reset the otghub urb status*/
+ huburb->status = -EINPROGRESS;
+ return 0;
+}
+EXPORT_SYMBOL(phci_remotewakeup);
+
+/*host controller wakeup sequence at otg port*/
+int
+phci_resume_wakeup(struct isp1763_dev *dev)
+{
+ printk("phci_wakeup_otg_port is called\n");
+#if 0
+ hubdev->otgstate = USB_OTG_WAKEUP_ALL;
+ if(huburb->status == -EINPROGRESS)
+#endif
+ huburb->status = 0;
+ /*complete the urb*/
+#if ((defined LINUX_269) || defined (LINUX_2611))
+ huburb->complete(huburb,NULL);
+#else
+ huburb->complete(huburb);
+#endif
+ /*reset the otghub urb status*/
+ huburb->status = -EINPROGRESS;
+ return 0;
+}
+EXPORT_SYMBOL(phci_resume_wakeup);
+
+struct isp1763_driver *host_driver;
+struct isp1763_driver *device_driver;
+
+void
+pehci_delrhtimer(struct isp1763_dev *dev)
+{
+
+ struct usb_hcd *usb_hcd =
+ container_of(huburb->dev->parent->bus, struct usb_hcd, self);
+ del_timer_sync(&usb_hcd->rh_timer);
+ del_timer(&usb_hcd->rh_timer);
+
+}
+EXPORT_SYMBOL(pehci_delrhtimer);
+
+int
+pehci_Deinitialize(struct isp1763_dev *dev)
+{
+ dev -= 2;
+ if (dev->index == 0) {
+ if (dev->driver) {
+ if (dev->driver->powerdown) {
+ dev->driver->powerdown(dev);
+ }
+ }
+ }
+return 0;
+}
+EXPORT_SYMBOL(pehci_Deinitialize);
+
+int
+pehci_Reinitialize(struct isp1763_dev *dev)
+{
+
+ dev -= 2;
+ if (dev->index == 0) {
+ if(dev->driver->powerup){
+ dev->driver->powerup(dev);
+ }
+ }
+return 0;
+}
+EXPORT_SYMBOL(pehci_Reinitialize);
+
+
diff --git a/drivers/usb/host/isp1763-qtdptd.c b/drivers/usb/host/isp1763-qtdptd.c new file mode 100644 index 00000000000..34552fb22bc --- /dev/null +++ b/drivers/usb/host/isp1763-qtdptd.c @@ -0,0 +1,1315 @@ +/*
+* 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.
+*
+* This is a host controller driver file. QTD processing is handled here.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+
+/* Td managenment routines */
+
+#define QUEUE_HEAD_NOT_EMPTY 0x001
+
+
+/*free the location used by removed urb/endpoint*/
+static void
+phci_hcd_release_td_ptd_index(struct ehci_qh *qh)
+{
+ td_ptd_map_buff_t *td_ptd_buff = &td_ptd_map_buff[qh->type];
+ td_ptd_map_t *td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+ /*hold the global lock here */
+ td_ptd_map->state = TD_PTD_NEW;
+ qh->qh_state = QH_STATE_IDLE;
+ /*
+ set these values to NULL as schedule
+ is based on these values,
+ rather td_ptd_map state
+ */
+ td_ptd_map->qh = NULL;
+ td_ptd_map->qtd = NULL;
+
+ td_ptd_buff->active_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+ /* Only pending transfers on current QH must be cleared */
+ td_ptd_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
+
+ pehci_entry("-- %s: Exit\n", __FUNCTION__);
+
+}
+
+/*print ehciqtd*/
+static void
+print_ehci_qtd(struct ehci_qtd *qtd)
+{
+ pehci_print("hwnext 0x%08x, altnext 0x%08x,token 0x%08x, length %d\n",
+ qtd->hw_next, qtd->hw_alt_next,
+ le32_to_cpu(qtd->hw_token), qtd->length);
+
+ pehci_print("buf[0] 0x%08x\n", qtd->hw_buf[0]);
+
+}
+
+/*delete all qtds linked with this urb*/
+static void
+phci_hcd_qtd_list_free(phci_hcd * ehci,
+ struct urb *urb, struct list_head *qtd_list)
+{
+ struct list_head *entry, *temp;
+
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+
+ list_for_each_safe(entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+ qtd = list_entry(entry, struct ehci_qtd, qtd_list);
+ if(!list_empty(&qtd->qtd_list))
+ list_del_init(&qtd->qtd_list);
+ qha_free(qha_cache, qtd);
+ }
+
+ pehci_entry("-- %s: Exit \n", __FUNCTION__);
+}
+
+
+/*
+ * free all the qtds for this transfer, also
+ * free the Host memory to be reused
+ */
+static void
+phci_hcd_urb_free_priv(phci_hcd * hcd,
+ urb_priv_t * urb_priv_to_remove, struct ehci_qh *qh)
+{
+ int i = 0;
+ struct ehci_qtd *qtd;
+ for (i = 0; i < urb_priv_to_remove->length; i++) {
+ if (urb_priv_to_remove->qtd[i]) {
+ qtd = urb_priv_to_remove->qtd[i];
+
+ if(!list_empty(&qtd->qtd_list))
+ list_del_init(&qtd->qtd_list);
+
+ /* This is required when the device is abruptly disconnected and the
+ * PTDs are not completely processed
+ */
+ if (qtd->length)
+ phci_hcd_mem_free(&qtd->mem_addr);
+
+ qha_free(qha_cache, qtd);
+ urb_priv_to_remove->qtd[i] = 0;
+ qtd = 0;
+ }
+
+ }
+
+ return;
+}
+
+
+/*allocate the qtd*/
+struct ehci_qtd *
+phci_hcd_qtd_allocate(int mem_flags)
+{
+
+ struct ehci_qtd *qtd = 0;
+ qtd = kmalloc(sizeof *qtd, mem_flags);
+ if (!qtd)
+ {
+ return 0;
+ }
+
+ memset(qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = cpu_to_le32(qtd);
+ qtd->hw_next = EHCI_LIST_END;
+ qtd->hw_alt_next = EHCI_LIST_END;
+ qtd->state = QTD_STATE_NEW;
+ INIT_LIST_HEAD(&qtd->qtd_list);
+ return qtd;
+}
+
+/*
+ * calculates host memory for current length transfer td,
+ * maximum td length is 4K(custom made)
+ * */
+static int
+phci_hcd_qtd_fill(struct urb *urb,
+ struct ehci_qtd *qtd,
+ dma_addr_t buf, size_t len, int token, int *status)
+{
+ int count = 0;
+
+ qtd->hw_buf[0] = (u32) buf;
+ /*max lenggth is HC_ATL_PL_SIZE */
+ if (len > HC_ATL_PL_SIZE) {
+ count = HC_ATL_PL_SIZE;
+ } else {
+ count = len;
+ }
+ qtd->hw_token = cpu_to_le32((count << 16) | token);
+ qtd->length = count;
+
+ pehci_print("%s:qtd %p, token %8x bytes %d dma %x\n",
+ __FUNCTION__, qtd, le32_to_cpu(qtd->hw_token), count,
+ qtd->hw_buf[0]);
+
+ return count;
+}
+
+
+/*
+ * makes number of qtds required for
+ * interrupt/bulk/control transfer length
+ * and initilize qtds
+ * */
+struct list_head *
+phci_hcd_make_qtd(phci_hcd * hcd,
+ struct list_head *head, struct urb *urb, int *status)
+{
+
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf, map_buf;
+ int len, maxpacket;
+ int is_input;
+ u32 token;
+ int cnt = 0;
+ urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+ pehci_entry("++ %s, Entered\n", __FUNCTION__);
+
+ /*take the qtd from already allocated
+ structure from hcd_submit_urb
+ */
+ qtd = urb_priv->qtd[cnt];
+ if (unlikely(!qtd)) {
+ *status = -ENOMEM;
+ return 0;
+ }
+
+ qtd_prev = 0;
+ list_add_tail(&qtd->qtd_list, head);
+
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+
+ len = urb->transfer_buffer_length;
+
+ is_input = usb_pipein(urb->pipe);
+
+ if (usb_pipecontrol(urb->pipe)) {
+ /* SETUP pid */
+ if (phci_hcd_qtd_fill(urb, qtd, cpu_to_le32(urb->setup_packet),
+ sizeof(struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8),
+ status) < 0) {
+ goto cleanup;
+ }
+
+ cnt++; /* increment the index */
+ print_ehci_qtd(qtd);
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = urb_priv->qtd[cnt];
+ if (unlikely(!qtd)) {
+ *status = -ENOMEM;
+ goto cleanup;
+ }
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ len = urb->transfer_buffer_length;
+ if (likely(len > 0)) {
+ /*update the buffer address */
+ buf = cpu_to_le32(urb->transfer_buffer);
+ } else {
+ buf = map_buf = cpu_to_le32(0); /*set-up stage has no data. */
+ }
+
+ /* So are we waiting for the ack only or there is a data stage with out. */
+ if (!buf || usb_pipein(urb->pipe)) {
+ token |= (1 /* "in" */ << 8);
+ }
+ /* else it's already initted to "out" pid (0 << 8) */
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe)) & 0x07ff;
+
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+
+ for (;;) {
+ int this_qtd_len;
+ this_qtd_len =
+ phci_hcd_qtd_fill(urb, qtd, buf, len, token, status);
+ if (this_qtd_len < 0)
+ goto cleanup;
+ print_ehci_qtd(qtd);
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+ cnt++;
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) {
+ token ^= QTD_TOGGLE;
+ }
+
+ if (likely(len <= 0)) {
+ break;
+ }
+ qtd_prev = qtd;
+ qtd = urb_priv->qtd[cnt];
+ if (unlikely(!qtd)) {
+ goto cleanup;
+ }
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+ }
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * bulk ones may need a terminating short packet (zero length).
+ */
+ if (likely(buf != 0)) {
+ int one_more = 0;
+ if (usb_pipecontrol(urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+
+ } else if (usb_pipebulk(urb->pipe) /* bulk data exactly terminated on zero lenth */
+ &&(urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket)) {
+ one_more = 1;
+ }
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = urb_priv->qtd[cnt];
+ if (unlikely(!qtd)) {
+ goto cleanup;
+ }
+
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+ phci_hcd_qtd_fill(urb, qtd, 0, 0, token, status);
+ print_ehci_qtd(qtd);
+ cnt++;
+ }
+ }
+
+ /*this is our last td for current transfer */
+ qtd->state |= QTD_STATE_LAST;
+
+ /*number of tds */
+ if (urb_priv->length != cnt) {
+ err("Never Error: number of tds allocated %d exceeding %d\n",
+ urb_priv->length, cnt);
+ }
+ /* by default, enable interrupt on urb completion */
+ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT))) {
+ qtd->hw_token |= __constant_cpu_to_le32(QTD_IOC);
+ }
+
+ pehci_entry("-- %s, Exit\n", __FUNCTION__);
+ return head;
+
+ cleanup:
+ phci_hcd_qtd_list_free(hcd, urb, head);
+ return 0;
+}
+
+/*allocates a queue head(endpoint*/
+struct ehci_qh *
+phci_hcd_qh_alloc(phci_hcd * hcd)
+{
+
+ struct ehci_qh *qh = kmalloc(sizeof(struct ehci_qh), GFP_ATOMIC);
+ if (!qh)
+ {
+ return qh;
+ }
+
+ memset(qh, 0, sizeof *qh);
+ atomic_set(&qh->refcount, 1);
+ init_waitqueue_head(&qh->waitforcomplete);
+ qh->qh_dma = (u32) qh;
+ INIT_LIST_HEAD(&qh->qtd_list);
+ INIT_LIST_HEAD(&qh->itd_list);
+ qh->next_uframe = -1;
+ return qh;
+}
+
+/* calculates header address for the tds*/
+static int
+phci_hcd_fill_ptd_addresses(td_ptd_map_t * td_ptd_map, int index, int bufftype)
+{
+ int i = 0;
+ unsigned long tdlocation = 0;
+ /*
+ * the below payloadlocation and
+ * payloadsize are redundant
+ * */
+ unsigned long payloadlocation = 0;
+ unsigned long payloadsize = 0;
+ pehci_entry("++ %s: enter\n", __FUNCTION__);
+ switch (bufftype) {
+ /*atl header starts at 0xc00 */
+ case TD_PTD_BUFF_TYPE_ATL:
+ tdlocation = 0x0c00;
+ /*redundant */
+ payloadsize = 0x1000;
+ payloadlocation = 0x1000;
+ break;
+ case TD_PTD_BUFF_TYPE_INTL:
+ /*interrupt header
+ * starts at 0x800
+ * */
+ tdlocation = 0x0800;
+ /*redundant */
+ payloadlocation = 0x1000;
+ payloadsize = 0x1000;
+ break;
+
+ case TD_PTD_BUFF_TYPE_ISTL:
+ /*iso header starts
+ * at 0x400*/
+
+ tdlocation = 0x0400;
+ /*redunndant */
+ payloadlocation = 0x1000;
+ payloadsize = 0x1000;
+
+ break;
+ }
+
+
+ i = index;
+ payloadlocation += (i) * payloadsize; /*each payload is of 4096 bytes */
+ tdlocation += (i) * PHCI_QHA_LENGTH; /*each td is of 32 bytes */
+ td_ptd_map->ptd_header_addr = tdlocation;
+ td_ptd_map->ptd_data_addr = payloadlocation;
+ td_ptd_map->ptd_ram_data_addr = ((payloadlocation - 0x0400) >> 3);
+ pehci_print
+ ("Index: %d, Header: 0x%08x, Payload: 0x%08x,Data start address: 0x%08x\n",
+ index, td_ptd_map->ptd_header_addr, td_ptd_map->ptd_data_addr,
+ td_ptd_map->ptd_ram_data_addr);
+ pehci_entry("-- %s: Exit", __FUNCTION__);
+ return payloadlocation;
+}
+
+
+/*--------------------------------------------------------------*
+ * calculate the header location for the current
+ * endpoint, if found returns a valid index
+ * else invalid
+ -----------------------------------------------------------*/
+static void
+phci_hcd_get_qtd_ptd_index(struct ehci_qh *qh,
+ struct ehci_qtd *qtd, struct ehci_itd *itd)
+{
+ u8 buff_type = td_ptd_pipe_x_buff_type[qh->type];
+ u8 qtd_ptd_index; /*, index; */
+ /*this is the location of the ptd's skip map/done map, also
+ calculating the td header, payload, data start address
+ location */
+ u8 bitmap = 0x1;
+ u8 max_ptds;
+
+ td_ptd_map_buff_t *ptd_map_buff = &(td_ptd_map_buff[buff_type]);
+ pehci_entry("++ %s, Entered, buffer type %d\n", __FUNCTION__,
+ buff_type);
+
+ /* ATL PTDs can wait */
+ max_ptds = (buff_type == TD_PTD_BUFF_TYPE_ATL)
+ ? TD_PTD_MAX_BUFF_TDS : ptd_map_buff->max_ptds;
+
+ for (qtd_ptd_index = 0; qtd_ptd_index < max_ptds; qtd_ptd_index++) { /* Find the first free slot */
+ if (ptd_map_buff->map_list[qtd_ptd_index].state == TD_PTD_NEW) {
+ /* Found a free slot */
+ if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+ qh->qtd_ptd_index = qtd_ptd_index;
+ }
+ ptd_map_buff->map_list[qtd_ptd_index].datatoggle = 0;
+ /*put the ptd_index into operational state */
+ ptd_map_buff->map_list[qtd_ptd_index].state =
+ TD_PTD_ACTIVE;
+ ptd_map_buff->map_list[qtd_ptd_index].qtd = qtd;
+ /* No td transfer is in progress */
+ ptd_map_buff->map_list[qtd_ptd_index].itd = itd;
+ /*initialize endpoint(queuehead) */
+ ptd_map_buff->map_list[qtd_ptd_index].qh = qh;
+ ptd_map_buff->map_list[qtd_ptd_index].ptd_bitmap =
+ bitmap << qtd_ptd_index;
+ phci_hcd_fill_ptd_addresses(&ptd_map_buff->
+ map_list[qtd_ptd_index],
+ qh->qtd_ptd_index,
+ buff_type);
+ ptd_map_buff->map_list[qtd_ptd_index].lasttd = 0;
+ ptd_map_buff->total_ptds++; /* update # of total td's */
+ /*make the queuehead map, to process in the phci_schedule_ptds */
+ ptd_map_buff->active_ptd_bitmap |=
+ (bitmap << qtd_ptd_index);
+ break;
+ }
+ }
+ pehci_entry("-- %s, Exit\n", __FUNCTION__);
+ return;
+
+} /* phci_get_td_ptd_index */
+
+
+
+/*
+ * calculate the header location for the endpoint and
+ * all tds on this endpoint will use the same
+ * header location for all transfers on this endpoint.
+ * also puts the endpoint into the linked state
+ * */
+static void
+phci_hcd_qh_link_async(phci_hcd * hcd, struct ehci_qh *qh, int *status)
+{
+ struct ehci_qtd *qtd = 0;
+ struct list_head *qtd_list = &qh->qtd_list;
+
+#ifdef MSEC_INT_BASED
+ td_ptd_map_buff_t *ptd_map_buff;
+ td_ptd_map_t *td_ptd_map;
+#endif
+
+ /* take the first td, in case we are not able to schedule the new td
+ and this is going for remove
+ */
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+
+ /* Assign a td-ptd index for this ed so that we can put ptd's in the HC buffers */
+
+ qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+ phci_hcd_get_qtd_ptd_index(qh, qtd, NULL); /* Get a td-ptd index */
+ if (qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX) {
+ err("can not find the location in our buffer\n");
+ *status = -ENOSPC;
+ return;
+ }
+#ifdef MSEC_INT_BASED
+ /*first transfers in sof interrupt goes into pending */
+ ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+ td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+ ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+
+#endif
+ /* open the halt so that it acessed */
+ qh->hw_token &= ~__constant_cpu_to_le32(QTD_STS_HALT);
+ qh->qh_state = QH_STATE_LINKED;
+ qh->qh_state |= QH_STATE_TAKE_NEXT;
+ pehci_entry("-- %s: Exit , qh %p\n", __FUNCTION__, qh);
+
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * mainly used for setting up current td on current
+ * endpoint(queuehead), endpoint may be new or
+ * halted one
+ * */
+
+static inline void
+phci_hcd_qh_update(phci_hcd * ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ /*make this current td */
+ qh->hw_current = QTD_NEXT(qtd->qtd_dma);
+ qh->hw_qtd_next = QTD_NEXT(qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END;
+ /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+ wmb();
+ qh->hw_token &= __constant_cpu_to_le32(QTD_TOGGLE | QTD_STS_PING);
+}
+
+/*
+ * used for ATL, INT transfers
+ * function creates new endpoint,
+ * calculates bandwidth for interrupt transfers,
+ * and initialize the qh based on endpoint type/speed
+ * */
+struct ehci_qh *
+phci_hcd_make_qh(phci_hcd * hcd,
+ struct urb *urb, struct list_head *qtd_list, int *status)
+{
+ struct ehci_qh *qh = 0;
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+ int mult = 0;
+ int bustime = 0;
+ struct ehci_qtd *qtd =
+ list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+
+
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+
+ qh = phci_hcd_qh_alloc(hcd);
+ if (!qh) {
+ *status = -ENOMEM;
+ return 0;
+ }
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint(urb->pipe) << 8;
+ info1 |= usb_pipedevice(urb->pipe) << 0;
+
+ is_input = usb_pipein(urb->pipe);
+ type = usb_pipetype(urb->pipe);
+ maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+ mult = 1 + ((maxp >> 11) & 0x3);
+
+ /*set this queueheads index to invalid */
+ qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
+
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ qh->type = TD_PTD_BUFF_TYPE_ATL;
+ break;
+
+ case PIPE_INTERRUPT:
+ qh->type = TD_PTD_BUFF_TYPE_INTL;
+ break;
+ case PIPE_ISOCHRONOUS:
+ qh->type = TD_PTD_BUFF_TYPE_ISTL;
+ break;
+
+ }
+
+
+
+ if (type == PIPE_INTERRUPT) {
+ /*for this interrupt transfer check how much bustime in usecs required */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ bustime = usb_check_bandwidth(urb->dev, urb);
+
+ if (bustime < 0) {
+ *status = -ENOSPC;
+ goto done;
+ }
+
+ usb_claim_bandwidth(urb->dev, urb, bustime,
+ usb_pipeisoc(urb->pipe));
+#else
+#endif
+ qh->usecs = bustime;
+
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+ /*after how many uframes this interrupt is to be executed */
+ qh->period = urb->interval >> 3;
+ if (qh->period < 1) {
+ printk("intr period %d uframes,\n",
+ urb->interval);
+ }
+ /*restore the original urb->interval in qh->period */
+ qh->period = urb->interval;
+
+ } else {
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + 7; /*usb_calc_bus_time (urb->dev->speed,
+ is_input, 0, maxp) / (125 * 1000); */
+
+ if (is_input) { /* SPLIT, gap, CSPLIT+DATA */
+
+ qh->c_usecs = qh->usecs + 1; /*HS_USECS (0); */
+ qh->usecs = 10; /*HS_USECS (1); */
+ } else { /* SPLIT+DATA, gap, CSPLIT */
+ qh->usecs += 10; /*HS_USECS (1); */
+ qh->c_usecs = 1; /*HS_USECS (0); */
+ }
+
+
+ /*take the period ss/cs scheduling will be
+ handled by submit urb
+ */
+ qh->period = urb->interval;
+ }
+ }
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= (1 << 12); /* EPS "low" */
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT) {
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ }
+ if (type == PIPE_CONTROL) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+ info2 |= urb->dev->ttport << 23;
+ info2 |= urb->dev->tt->hub->devnum << 16;
+ break;
+
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+
+ info1 |= 1 << 14; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 512 << 16; /* usb2 fixed maxpacket */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else { /* PIPE_INTERRUPT */
+ info1 |= (maxp & 0x7ff) /*max_packet (maxp) */ <<16;
+ info2 |= mult /*hb_mult (maxp) */ << 30;
+ }
+ break;
+
+ default:
+ pehci_print("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ done:
+#else
+#endif
+ qha_free(qha_cache, qh);
+ return 0;
+ } /*end of switch */
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as halted, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_info1 = cpu_to_le32(info1);
+ qh->hw_info2 = cpu_to_le32(info2);
+ /*link the tds here */
+ list_splice(qtd_list, &qh->qtd_list);
+ phci_hcd_qh_update(hcd, qh, qtd);
+ qh->hw_token = cpu_to_le32(QTD_STS_HALT);
+ if (!usb_pipecontrol(urb->pipe)) {
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input,
+ 1);
+ }
+ pehci_entry("-- %s: Exit, qh %p\n", __FUNCTION__, qh);
+ return qh;
+}
+
+
+/*-----------------------------------------------------------*/
+/*
+ * Hardware maintains data toggle (like OHCI) ... here we (re)initialize
+ * the hardware data toggle in the QH, and set the pseudo-toggle in udev
+ * so we can see if usb_clear_halt() was called. NOP for control, since
+ * we set up qh->hw_info1 to always use the QTD toggle bits.
+ */
+static inline void
+phci_hcd_clear_toggle(struct usb_device *udev, int ep, int is_out,
+ struct ehci_qh *qh)
+{
+ pehci_print("clear toggle, dev %d ep 0x%x-%s\n",
+ udev->devnum, ep, is_out ? "out" : "in");
+ qh->hw_token &= ~__constant_cpu_to_le32(QTD_TOGGLE);
+ usb_settoggle(udev, ep, is_out, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+struct ehci_qh *
+phci_hcd_qh_append_tds(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ struct usb_host_endpoint *ep,
+#else
+#endif
+ struct urb *urb, struct list_head *qtd_list,
+ void **ptr, int *status)
+{
+
+ int epnum;
+
+ struct ehci_qh *qh = 0;
+ struct ehci_qtd *qtd =
+ list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ td_ptd_map_buff_t *ptd_map_buff;
+ td_ptd_map_t *td_ptd_map;
+
+
+
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ epnum = ep->desc.bEndpointAddress;
+#else
+ epnum = urb->ep->desc.bEndpointAddress;
+#endif
+
+ qh = (struct ehci_qh *) *ptr;
+ if (likely(qh != 0)) {
+ u32 hw_next = QTD_NEXT(qtd->qtd_dma);
+ pehci_print("%Queue head already %p\n", qh);
+
+ ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+ td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+
+ /* maybe patch the qh used for set_address */
+ if (unlikely
+ (epnum == 0 && le32_to_cpu(qh->hw_info1 & 0x7f) == 0)) {
+ qh->hw_info1 |= cpu_to_le32(usb_pipedevice(urb->pipe));
+ }
+
+ /* is an URB is queued to this qh already? */
+ if (unlikely(!list_empty(&qh->qtd_list))) {
+ struct ehci_qtd *last_qtd;
+ /* update the last qtd's "next" pointer */
+ last_qtd = list_entry(qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+
+ /*queue head is not empty just add the
+ td at the end of it , and return from here
+ */
+ last_qtd->hw_next = hw_next;
+
+ /*set the status as positive */
+ *status = (u32) QUEUE_HEAD_NOT_EMPTY;
+
+ /* no URB queued */
+ } else {
+
+ // qh->qh_state = QH_STATE_IDLE;
+
+
+ /* usb_clear_halt() means qh data toggle gets reset */
+ if (usb_pipebulk(urb->pipe)
+ && unlikely(!usb_gettoggle(urb->dev, (epnum & 0x0f),
+ !(epnum & 0x80)))) {
+
+ phci_hcd_clear_toggle(urb->dev,
+ epnum & 0x0f,
+ !(epnum & 0x80), qh);
+
+ /*reset our data toggle */
+
+ qh->datatoggle = 0;
+ qh->ping = 0;
+
+ }
+ phci_hcd_qh_update(hcd, qh, qtd);
+ }
+ /*put everything in pedning, will be cleared during scheduling */
+ ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
+ list_splice(qtd_list, qh->qtd_list.prev);
+ } else {
+ qh = phci_hcd_make_qh(hcd, urb, qtd_list, status);
+ *ptr = qh;
+ }
+ pehci_entry("-- %s: Exit qh %p\n", __FUNCTION__, qh);
+ return qh;
+}
+
+/*link qtds to endpoint(qh)*/
+struct ehci_qh *
+phci_hcd_submit_async(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ struct usb_host_endpoint *ep,
+#else
+#endif
+ struct list_head *qtd_list, struct urb *urb, int *status)
+{
+ struct ehci_qtd *qtd;
+ struct hcd_dev *dev;
+ int epnum;
+
+#ifndef THREAD_BASED
+ unsigned long flags;
+#endif
+
+
+ struct ehci_qh *qh = 0;
+
+ urb_priv_t *urb_priv = urb->hcpriv;
+
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ dev = (struct hcd_dev *) urb->hcpriv;
+ epnum = usb_pipeendpoint(urb->pipe);
+ if (usb_pipein(urb->pipe) && !usb_pipecontrol(urb->pipe)) {
+ epnum |= 0x10;
+ }
+
+ pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+ /* ehci_hcd->lock guards shared data against other CPUs:
+ * ehci_hcd: async, reclaim, periodic (and shadow), ...
+ * hcd_dev: ep[]
+
+ * ehci_qh: qh_next, qtd_list
+
+ * ehci_qtd: qtd_list
+ *
+ * Also, hold this lock when talking to HC registers or
+ * when updating hw_* fields in shared qh/qtd/... structures.
+ */
+#ifndef THREAD_BASED
+ spin_lock_irqsave(&hcd->lock, flags);
+#endif
+
+ spin_lock(&hcd_data_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ usb_hcd_link_urb_to_ep(&hcd->usb_hcd, urb);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ qh = phci_hcd_qh_append_tds(hcd, ep, urb, qtd_list, &ep->hcpriv,
+ status);
+#else
+ qh = phci_hcd_qh_append_tds(hcd, urb, qtd_list, &urb->ep->hcpriv,
+ status);
+#endif
+ if (!qh || *status < 0) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+ usb_hcd_unlink_urb_from_ep(&hcd->usb_hcd, urb);
+#endif
+ goto cleanup;
+ }
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+
+ /* now the quehead can not be in the unlink state */
+
+// printk("qh->qh_state:0x%x \n",qh->qh_state);
+ if (qh->qh_state == QH_STATE_UNLINK) {
+ pehci_info("%s: free the urb,qh->state %x\n", __FUNCTION__,
+ qh->qh_state);
+ phci_hcd_qtd_list_free(hcd, urb, &qh->qtd_list);
+ spin_unlock(&hcd_data_lock);
+
+#ifndef THREAD_BASED
+ spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+ *status = -ENODEV;
+ return 0;
+ }
+
+ if (likely(qh != 0)) {
+ urb_priv->qh = qh;
+ if (likely(qh->qh_state == QH_STATE_IDLE))
+ phci_hcd_qh_link_async(hcd, qh, status);
+ }
+
+ cleanup:
+ spin_unlock(&hcd_data_lock);
+
+#ifndef THREAD_BASED
+ /* free it from lock systme can sleep now */
+ spin_unlock_irqrestore(&hcd->lock, flags);
+#endif
+
+ /* could not get the QH terminate and clean. */
+ if (unlikely(qh == 0) || *status < 0) {
+ phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+ return qh;
+ }
+ return qh;
+}
+
+/*
+ * initilaize the s-mask c-mask for
+ * interrupt transfers.
+ */
+static int
+phci_hcd_qhint_schedule(phci_hcd * hcd,
+ struct ehci_qh *qh,
+ struct ehci_qtd *qtd,
+ struct _isp1763_qhint *qha, struct urb *urb)
+{
+ int i = 0;
+ u32 td_info3 = 0;
+ u32 td_info5 = 0;
+ u32 period = 0;
+ u32 usofmask = 1;
+ u32 usof = 0;
+ u32 ssplit = 0, csplit = 0xFF;
+ int maxpacket;
+ u32 numberofusofs = 0;
+
+ /*and since whol msec frame is empty, i can schedule in any uframe */
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe));
+ maxpacket &= 0x7ff;
+ /*length of the data per uframe */
+ maxpacket = XFER_PER_UFRAME(qha->td_info1) * maxpacket;
+
+ /*caculate the number of uframes are required */
+ numberofusofs = urb->transfer_buffer_length / maxpacket;
+ /*if something left */
+ if (urb->transfer_buffer_length % maxpacket) {
+ numberofusofs += 1;
+ }
+
+ for (i = 0; i < numberofusofs; i++) {
+ usofmask <<= i;
+ usof |= usofmask;
+
+ }
+
+ /*
+ for full/low speed devices, as we
+ have seperate location for all the endpoints
+ let the start split goto the first uframe, means 0 uframe
+ */
+ if (urb->dev->speed != USB_SPEED_HIGH && usb_pipeint(urb->pipe)) {
+ /*set the complete splits */
+ /*set all the bits and lets see whats happening */
+ /*but this will be set based on the maximum packet size */
+ ssplit = usof;
+ /* need to fix it */
+ csplit = 0x1C;
+ qha->td_info6 = csplit;
+ period = qh->period;
+ if (period >= 32) {
+ period = qh->period / 2;
+ }
+ td_info3 = period;
+ goto done;
+
+ } else {
+ if (qh->period >= 8) {
+ period = qh->period / 8;
+ } else {
+ period = qh->period;
+ }
+ }
+ /*our limitaion is maximum of 32 ie 31, 5 bits */
+ if (period >= 32) {
+ period = 32;
+ /*devide by 2 */
+ period >>= 1;
+ }
+ if (qh->period >= 8) {
+ /*millisecond period */
+ td_info3 = (period << 3);
+ } else {
+ /*usof based tranmsfers */
+ /*minimum 4 usofs */
+ td_info3 = period;
+ usof = 0x11;
+ }
+
+ done:
+ td_info5 = usof;
+ qha->td_info3 |= td_info3;
+ qha->td_info5 |= usof;
+ return numberofusofs;
+}
+
+/*link interrupts qtds to endpoint*/
+struct ehci_qh *
+phci_hcd_submit_interrupt(phci_hcd * hcd,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ struct usb_host_endpoint *ep,
+#else
+#endif
+ struct list_head *qtd_list,
+ struct urb *urb, int *status)
+{
+ struct ehci_qtd *qtd;
+ struct _hcd_dev *dev;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = 0;
+ urb_priv_t *urb_priv = (urb_priv_t *) urb->hcpriv;
+
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ dev = (struct hcd_dev *) urb->hcpriv;
+ epnum = ep->desc.bEndpointAddress;
+
+ pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+ /*check for more than one urb queued for this endpoint */
+ qh = ep->hcpriv;
+#else
+ dev = (struct _hcd_dev *) (urb->hcpriv);
+ epnum = urb->ep->desc.bEndpointAddress;
+
+ pehci_entry("++ %s, enter\n", __FUNCTION__);
+
+
+ /*check for more than one urb queued for this endpoint */
+ qh = (struct ehci_qh *) urb->ep->hcpriv;
+#endif
+
+ spin_lock_irqsave(&hcd->lock, flags);
+ if (unlikely(qh != 0)) {
+ if (!list_empty(&qh->qtd_list)) {
+ *status = -EBUSY;
+ goto done;
+ } else {
+ td_ptd_map_buff_t *ptd_map_buff;
+ td_ptd_map_t *td_ptd_map;
+ ptd_map_buff = &(td_ptd_map_buff[qh->type]);
+ td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
+ ptd_map_buff->pending_ptd_bitmap |=
+ td_ptd_map->ptd_bitmap;
+ /*NEW*/ td_ptd_map->qtd = qtd;
+ /* maybe reset hardware's data toggle in the qh */
+ if (unlikely(!usb_gettoggle(urb->dev, epnum & 0x0f,
+ !(epnum & 0x80)))) {
+
+ /*reset our data toggle */
+ td_ptd_map->datatoggle = 0;
+ usb_settoggle(urb->dev, epnum & 0x0f,
+ !(epnum & 0x80), 1);
+ qh->datatoggle = 0;
+ }
+ /* trust the QH was set up as interrupt ... */
+ list_splice(qtd_list, &qh->qtd_list);
+ }
+ }
+
+
+ if (!qh) {
+ qh = phci_hcd_make_qh(hcd, urb, qtd_list, status);
+ if (likely(qh == 0)) {
+ *status = -ENOMEM;
+ goto done;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+ ep->hcpriv = qh;
+#else
+ urb->ep->hcpriv = qh;
+#endif
+ }
+
+ if (likely(qh != 0)) {
+ urb_priv->qh = qh;
+ if (likely(qh->qh_state == QH_STATE_IDLE)) {
+ phci_hcd_qh_link_async(hcd, qh, status);
+ }
+ }
+
+
+ done:
+ /* free it from lock systme can sleep now */
+ spin_unlock_irqrestore(&hcd->lock, flags);
+ /* could not get the QH terminate and clean. */
+ if (unlikely(qh == 0) || *status < 0) {
+ phci_hcd_qtd_list_free(hcd, urb, qtd_list);
+ return qh;
+ }
+ return qh;
+}
+
+
+
+
+/*
+ * converts original EHCI QTD into PTD(Proprietary transfer descriptor)
+ * we call PTD as qha also for atl transfers
+ * for ATL and INT transfers
+ */
+void *
+phci_hcd_qha_from_qtd(phci_hcd * hcd,
+ struct ehci_qtd *qtd,
+ struct urb *urb,
+ void *ptd, u32 ptd_data_addr, struct ehci_qh *qh)
+{
+ u8 toggle = qh->datatoggle;
+ u32 token = 0;
+ u32 td_info1 = 0;
+ u32 td_info3 = 0;
+ u32 td_info4 = 0;
+ int maxpacket = 0;
+ u32 length = 0, temp = 0;
+ /*for non high speed devices */
+ u32 portnum = 0;
+ u32 hubnum = 0;
+ u32 se = 0, rl = 0x0, nk = 0x0;
+ u8 datatoggle = 0;
+ struct isp1763_mem_addr *mem_addr = &qtd->mem_addr;
+ u32 data_addr = 0;
+ u32 multi = 0;
+ struct _isp1763_qha *qha = (isp1763_qha *) ptd;
+ pehci_entry("++ %s: Entered\n", __FUNCTION__);
+
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+ multi = 1 + ((maxpacket >> 11) & 0x3);
+
+ maxpacket &= 0x7ff;
+
+ /************************first word*********************************/
+ length = qtd->length;
+ td_info1 = QHA_VALID;
+ td_info1 |= (length << 3);
+ td_info1 |= (maxpacket << 18);
+ td_info1 |= (usb_pipeendpoint(urb->pipe) << 31);
+ td_info1 |= MULTI(multi);
+ /*set the first dword */
+ qha->td_info1 = td_info1;
+
+ pehci_print("%s: length %d, 1st word 0x%08x\n", __FUNCTION__, length,
+ qha->td_info1);
+
+ /*******************second word***************************************/
+ temp = qtd->hw_token;
+
+ /*take the pid, thats of only interest to me from qtd,
+ */
+
+ temp = temp & 0x0300;
+ temp = temp >> 8;
+ /*take the endpoint and its 3 bits */
+ token = (usb_pipeendpoint(urb->pipe) & 0xE) >> 1;
+ token |= usb_pipedevice(urb->pipe) << 3;
+
+ if (urb->dev->speed != USB_SPEED_HIGH) {
+ pehci_print("device is full/low speed, %d\n", urb->dev->speed);
+ token |= 1 << 14;
+ portnum = urb->dev->ttport;
+ /*IMMED*/ hubnum = urb->dev->tt->hub->devnum;
+ token |= portnum << 18;
+ token |= hubnum << 25;
+ /*for non-high speed transfer
+ reload and nak counts are zero
+ */
+ rl = 0x0;
+ nk = 0x0;
+
+ }
+
+ /*se should be 0x2 for only low speed devices */
+ if (urb->dev->speed == USB_SPEED_LOW) {
+ se = 0x2;
+ }
+
+ if (usb_pipeint(urb->pipe)) {
+ /* reload count and nakcount is
+ required for only async transfers
+ */
+ rl = 0x0;
+ }
+
+ /*set the se field, should be zero for all
+ but low speed devices
+ */
+ token |= se << 16;
+ /*take the pid */
+ token |= temp << 10;
+
+ if (usb_pipebulk(urb->pipe)) {
+ token |= EPTYPE_BULK;
+ } else if (usb_pipeint(urb->pipe)) {
+ token |= EPTYPE_INT;
+ } else if (usb_pipeisoc(urb->pipe)) {
+ token |= EPTYPE_ISO;
+ }
+
+
+ qha->td_info2 = token;
+
+ pehci_print("%s: second word 0x%08x, qtd token 0x%08x\n",
+ __FUNCTION__, qha->td_info2, temp);
+
+ /***********************Third word*************************************/
+
+ /*calculate the data start address from mem_addr for qha */
+
+ data_addr = ((u32) (mem_addr->phy_addr) & 0xffff) - 0x400;
+ data_addr >>= 3;
+ pehci_print("data start address %x\n", data_addr);
+ /*use this field only if there
+ * is something to transfer
+ * */
+ if (length) {
+ td_info3 = data_addr << 8;
+ }
+ /*RL Count, 16 */
+ td_info3 |= (rl << 25);
+ qha->td_info3 = td_info3;
+
+ pehci_print("%s: third word 0x%08x, tdinfo 0x%08x\n",
+ __FUNCTION__, qha->td_info3, td_info3);
+
+
+ /**************************fourt word*************************************/
+
+ if (usb_pipecontrol(urb->pipe)) {
+ datatoggle = qtd->hw_token >> 31;
+ } else {
+ /*take the data toggle from the previous completed transfer
+ or zero in case of fresh */
+ datatoggle = toggle;
+ }
+
+ td_info4 = QHA_ACTIVE;
+ /*dt */
+ td_info4 |= datatoggle << 25; /*QHA_DATA_TOGGLE; */
+ /*3 retry count for setup else forever */
+ if (PTD_PID(qha->td_info2) == SETUP_PID) {
+ td_info4 |= (3 << 23);
+ } else {
+ td_info4 |= (0 << 23);
+ }
+
+ /*nak count */
+ td_info4 |= (nk << 19);
+
+ td_info4 |= (qh->ping << 26);
+ qha->td_info4 = td_info4;
+#ifdef PTD_DUMP_SCHEDULE
+ printk("SCHEDULE PTD DUMPE\n") ;
+ printk("SDW0: 0x%08x\n",qha->td_info1);
+ printk("SDW1: 0x%08x\n",qha->td_info2);
+ printk("SDW2: 0x%08x\n",qha->td_info3);
+ printk("SDW3: 0x%08x\n",qha->td_info4);
+#endif
+ pehci_print("%s: fourt word 0x%08x\n", __FUNCTION__, qha->td_info4);
+ pehci_entry("-- %s: Exit, qha %p\n", __FUNCTION__, qha);
+ return qha;
+
+}
diff --git a/drivers/usb/host/isp1763.h b/drivers/usb/host/isp1763.h new file mode 100644 index 00000000000..ae4a9908905 --- /dev/null +++ b/drivers/usb/host/isp1763.h @@ -0,0 +1,224 @@ +/*
+* Copyright (C) ST-Ericsson AP Pte Ltd 2010
+*
+* ISP1763 Linux OTG Controller driver : hal
+*
+* 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.
+*
+* This is a hardware abstraction layer header file.
+*
+* Author : wired support <wired.support@stericsson.com>
+*
+*/
+
+#ifndef ISP1763_H
+#define ISP1763_H
+
+
+
+/* For debugging option: ------------------- */
+#define PTD_DUMP_SCHEDULE
+#undef PTD_DUMP_SCHEDULE
+
+#define PTD_DUMP_COMPLETE
+#undef PTD_DUMP_COMPLETE
+/* ------------------------------------*/
+#define CONFIG_ISO_SUPPORT
+
+#ifdef CONFIG_ISO_SUPPORT
+
+#define ISO_DBG_ENTRY 1
+#define ISO_DBG_EXIT 1
+#define ISO_DBG_ADDR 1
+#define ISO_DBG_DATA 1
+#define ISO_DBG_ERR 1
+#define ISO_DBG_INFO 1
+
+#if 0 /* Set to 1 to enable isochronous debugging */
+#define iso_dbg(category, format, arg...) \
+do \
+{ \
+ if(category) \
+ { \
+ printk(format, ## arg); \
+ } \
+} while(0)
+#else
+#define iso_dbg(category, format, arg...) while(0)
+#endif
+
+#endif /* CONFIG_ISO_SUPPORT */
+
+/*Debug For Entry/Exit of the functions */
+//#define HCD_DEBUG_LEVEL1
+#ifdef HCD_DEBUG_LEVEL1
+#define pehci_entry(format, args... ) printk(format, ##args)
+#else
+#define pehci_entry(format, args...) do { } while(0)
+#endif
+
+/*Debug for Port Info and Errors */
+//#define HCD_DEBUG_LEVEL2
+#ifdef HCD_DEBUG_LEVEL2
+#define pehci_print(format, args... ) printk(format, ##args)
+#else
+#define pehci_print(format, args...) do { } while(0)
+#endif
+
+/*Debug For the Port changes and Enumeration */
+//#define HCD_DEBUG_LEVEL3
+#ifdef HCD_DEBUG_LEVEL3
+#define pehci_info(format,arg...) printk(format, ##arg)
+#else
+#define pehci_info(format,arg...) do {} while (0)
+#endif
+
+/*Debug For Transfer flow */
+//#define HCD_DEBUG_LEVEL4
+#ifdef HCD_DEBUG_LEVEL4
+#define pehci_check(format,args...) printk(format, ##args)
+#else
+#define pehci_check(format,args...)
+#endif
+/*******************END HOST CONTROLLER**********************************/
+
+
+
+/*******************START DEVICE CONTROLLER******************************/
+
+/* For MTP support */
+#undef MTP_ENABLE /* Enable to add MTP support; But requires MTP class driver to be present to work */
+/*For CHAPTER8 TEST */
+#undef CHAPTER8_TEST /* Enable to Pass Chapter 8 Test */
+
+/* Debug Entery/Exit of Function as well as some other Info */
+//#define DEV_DEBUG_LEVEL2
+#ifdef DEV_DEBUG_LEVEL2
+#define dev_print(format,arg...) printk(format, ##arg)
+#else
+#define dev_print(format,arg...) do {} while (0)
+#endif
+
+/*Debug for Interrupt , Registers , device Enable/Disable and some other info */
+//#define DEV_DEBUG_LEVEL3
+//#ifdef DEV_DEBUG_LEVEL3
+//#define dev_info(format,arg...) printk(format, ##arg)
+//#else
+//#define dev_info(format,arg...) do {} while (0)
+//#endif
+
+/*Debug for Tranffer flow , Enumeration and Packet info */
+//#define DEV_DEBUG_LEVEL4
+#ifdef DEV_DEBUG_LEVEL4
+#define dev_check(format,args...) printk(format, ##args)
+#else
+#define dev_check(format,args...) do{}while(0)
+#endif
+/*******************END DEVICE CONTROLLER********************************/
+
+
+/*******************START MSCD*******************************************/
+/*Debug Entery/Exit of Function as well as some other Information*/
+//#define MSCD_DEBUG_LEVEL2
+#ifdef MSCD_DEBUG_LEVEL2
+#define mscd_print(format,arg...) printk(format, ##arg)
+#else
+#define mscd_print(format,arg...) do {} while (0)
+#endif
+
+/*Debug for Info */
+//#define MSCD_DEBUG_LEVEL3
+#ifdef MSCD_DEBUG_LEVEL3
+#define mscd_info(format,arg...) printk(format, ##arg)
+#else
+#define mscd_info(format,arg...) do {} while (0)
+#endif
+/*******************END MSCD*********************************************/
+
+
+/*******************START OTG CONTROLLER*********************************/
+#define OTG /*undef for Device only and Host only */
+#define ALL_FSM_FLAGS
+/*Debug for Entry/Exit and Info */
+/* #define OTG_DEBUG_LEVEL1 */
+#ifdef OTG_DEBUG_LEVEL1
+#define otg_entry(format, args... ) printk(format, ##args)
+#else
+#define otg_entry(format, args...) do { } while(0)
+#endif
+
+/*Debug for State Machine Flow */
+/* #define OTG_DEBUG_LEVEL2 */
+#ifdef OTG_DEBUG_LEVEL2
+#define otg_print(format,arg...) printk(format, ##arg)
+#else
+#define otg_print(format,arg...) do {} while (0)
+#endif
+/*Debug for Info */
+/* #define OTG_DEBUG_LEVEL3 */
+#ifdef OTG_DEBUG_LEVEL3
+#define otg_info(format,arg...) printk(format, ##arg)
+#else
+#define otg_info(format,arg...) do {} while (0)
+#endif
+
+/* #define OTG_DEBUG_LEVEL4 */
+#ifdef OTG_DEBUG_LEVEL4
+#define otg_printB(format,arg...) printk(format, ##arg)
+#else
+#define otg_printB(format,arg...) do {} while (0)
+#endif
+/*******************END OTG CONTROLLER***********************************/
+
+
+
+/*******************START FOR HAL ***************************************/
+/*Debug For Entry and Exit of the functions */
+//#define HAL_DEBUG_LEVEL1
+#ifdef HAL_DEBUG_LEVEL1
+#define hal_entry(format, args... ) printk(format, ##args)
+#else
+#define hal_entry(format, args...) do { } while(0)
+#endif
+
+/*Debug For Interrupt information */
+//#define HAL_DEBUG_LEVEL2
+#ifdef HAL_DEBUG_LEVEL2
+#define hal_int(format, args... ) printk(format, ##args)
+#else
+#define hal_int(format, args...) do { } while(0)
+#endif
+
+/*Debug For HAL Initialisation and Mem Initialisation */
+//#define HAL_DEBUG_LEVEL3
+#ifdef HAL_DEBUG_LEVEL3
+#define hal_init(format, args... ) printk(format, ##args)
+#else
+#define hal_init(format, args...) do { } while(0)
+#endif
+/*******************END FOR HAL*******************************************/
+
+
+
+/*******************START FOR ALL CONTROLLERS*****************************/
+#undef CONFIG_USB_OTG /*undef for Device only and Host only */
+#define ISP1763_DEVICE
+
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+/*******************END FOR ALL CONTROLLERS*******************************/
+#endif
|