ChangeSet 1.1148.6.5, 2003/10/06 15:54:07-07:00, henry.ne@arcor.de [PATCH] USB: Update SL811, HC_SL811 driver Update and Bug fix for driver sl811 and hc_sl811 Diff build from kerneltree linux-2.4.23-pre6 (02.10.2003). Please tell me, if I should separate something of this patch in diffetent files. Changes by Henry.Ne@Arcor.de: o General cleanup driver to 2.4.23 o Moving hc_sl811 into directory drivers/usb/host o Moving hardware inline functions into include/asm o Enable driver in Configuration and Make o Help for configure, general help for SL811 o Add architecture x86 o Two and more instances of controllers (x86 only) o Some bugfixed for hc_sl811 o Remove some warnings drivers/usb/hc_simple.c | 1072 ----------------------------- drivers/usb/hc_simple.h | 231 ------ drivers/usb/hc_sl811.c | 1359 ------------------------------------- drivers/usb/hc_sl811.h | 385 ---------- drivers/usb/hc_sl811_rh.c | 526 -------------- Documentation/Configure.help | 19 Documentation/usb/sl811hc.txt | 331 +++++++++ drivers/usb/Makefile | 3 drivers/usb/host/Config.in | 6 drivers/usb/host/Makefile | 1 drivers/usb/host/hc_simple.c | 1089 ++++++++++++++++++++++++++++++ drivers/usb/host/hc_simple.h | 230 ++++++ drivers/usb/host/hc_sl811.c | 1484 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/hc_sl811.h | 386 ++++++++++ drivers/usb/host/hc_sl811_rh.c | 536 ++++++++++++++ drivers/usb/host/sl811.c | 198 +++-- include/asm-arm/hc_sl811-hw.h | 139 +++ include/asm-arm/sl811-hw.h | 148 ++++ include/asm-i386/hc_sl811-hw.h | 139 +++ include/asm-i386/sl811-hw.h | 133 +++ 20 files changed, 4754 insertions(+), 3661 deletions(-) diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help --- a/Documentation/Configure.help Fri Oct 24 17:01:14 2003 +++ b/Documentation/Configure.help Fri Oct 24 17:01:14 2003 @@ -14639,6 +14639,25 @@ The module will be called usb-ohci.o. If you want to compile it as a module, say M here and read . +SL811HS (x86, StrongARM) support +CONFIG_USB_SL811HS + Embedded Open Host Controller SL811HS from CYPRESS SEMICONDUCTOR INC. + + + Board USB1104 in i386 architecture with PC/104-bus. + + + + StrongARM is currently not testet and not for PC/104-bus! + StrongARM need a special hardware with Chip Select directly from CPU. + See also SL811HS_ALT. + +SL811HS_ALT (x86, StrongARM) support +CONFIG_USB_SL811HS_ALT + Embedded Open Host Controller SL811HS from CYPRESS SEMICONDUCTOR INC. + Alternate with isochornous mode and better interrupt handling. + See also SL811HS. + USB Human Interface Device (full HID) support CONFIG_USB_HID Say Y here if you want full HID support to connect keyboards, diff -Nru a/Documentation/usb/sl811hc.txt b/Documentation/usb/sl811hc.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/usb/sl811hc.txt Fri Oct 24 17:01:14 2003 @@ -0,0 +1,331 @@ +README for embedded host controller SL811 (i386) +================================================ + +Original drivers from Pei Liu for ARM architecture only. +Documentaion and readme for Architecture x86 (ADNP/1486) premealy. + + +Kernel configuration: +--------------------- +o Patch USB drivers into your kerneltree + cd Your_kernel_root + + gunzip linux-2.4.20-usb-1.patch.gz + patch -p1 -T < linux-2.4.20-usb*.patch + OR + gunzip -dc linux-2.4.20-usb*.patch.gz | patch -p1 -T + +o Load default configuration for CP486SX/2 with ADNP/1486 and USB + arch/i386/adnp1486-usb104-SSV20030516 + +o Run "make configure" and set / verify this entries + Code maturity level options ---> + [*] Prompt for development and/or incomplete code/drivers + General setup ---> + [*] PCI support + (BIOS) PCI access mode + [ ] PCI device name database + SCSI support ---> + SCSI support + SCSI disk support + (8) Maximum number of SCSI disks that can be loaded as modules + ... Disable all other options ... + SCSI low-level drivers ---> + ... Disable all low level drivers ... + Input core support ---> + Input core support (Need only for Keyboard or Mouse) + Keyboard support + Mouse support + Character devices ---> + [*] Virtual terminal (Need only for Keyboard) + Console drivers ---> + [*] VGA text console (Need only for Key/Mouse) + Frame-buffer support ---> (... or use FB) + USB support ---> + Support for USB + [ ] USB verbose debug messages (Optional) + [*] Preliminary USB device filesystem (Optional for debugging) + --- USB Host Controller Drivers + SL811HS Alternate (support isochronous mode) + ... + SL811HS (x86, StrongARM) support (old driver) + Disable all others "USB Host Controller Drivers" + --- USB Device Class drivers + USB Mass Storage support + ... and some aditional devices ... + USB Printer support + USB HIDBP Keyboard (basic) support + USB HIDBP Mouse (basic) support + USB Serial Converter support ---> + USB Serial Converter support + USB FTDI Single Port Serial Driver +o We have no PCI- and no SCSI-System, but all USB-drivers need CONFIG_PCI=y. + USB-Floppy driver need the SCSI-Subsystem, so we must enable this global + and disable all low level drivers in this menu. +--- OR --- + Download origanal kerneltree, and patch comlplete ADNP + with USB support and load configuration for this: + bunzip2 -c linux-2.4.20.tar.bz2 | tar xvf - + gunzip -c linux-2.4.20-SSV20030516.diff.gz | patch -p1 -T + Configuration: arch/i386/adnp1486-usb104-SSV20030516 + +o Compile the kernel + make dep + make ROOT_DEV=/dev/ram0 zImage + make modules + export INSTALL_MOD_PATH="`pwd`/_install" ; make modules_install + + +Default device configuration hc_sl811.o for USB1/104: +----------------------------------------------------- +io = 0x220 +irq = 12 +Remember: Second Controller was handled internal with IO offset +2. + + +Installation hc_sl811.o for CF1/USB: +------------------------------------ +Compact Flash to USB adapter in io address of ide driver. It is for embedded +deviced only. +Please disable IDE driver in kernel configuration or do not load IDE drivers! +Change MAX_CONTROLERS = 1 into source asm/sl811-hw.h, recompile driver! + First Controller only + insmod hc_sl811.o io=0x1F0 irq=14 + Second Controller only + insmod hc_sl811.o io=0x3F6 irq=14 + +Driver hc_sl811.o can not handle both controllers at same time. +This driver need a address-offset of 2 between controllers. +Please use alternate driver sl811.o instand. + + +Installation Alternate driver sl811.o: +-------------------------------------- +This driver have a better interrupt handler, but don't tested with all devices. + +Install both controllers on USB1-104 (default): + insmod sl811.o io=0x220,0x222 irq=12,12 + +Install both controllers on CF/USB1: + insmod sl811.o io=0x1f0,0x3f6 irq=14,14 + +Second controller can disable with specific IOBASE=0 for this controller. + + +General about USB: +------------------ +Please install first the driver for hardware, +and than plugin the hardware into first USB port. + +If your hardware find no driver the usbcore give ub a massage for missing +driver on conole or in file /proc/kmsg such as: + new USB device -1.9, assigned address 7 + USB device 7 (vend/prod 0x403/0x8372) is not claimed by any active drive +In this case remove the hardware from USB port, install the driver and +than plugin hardware again. +A list of driver for this missing hardware can found in file +/_install/lib/modules/2.4.20/modules.usbmap +Search the number 8372 in this file an verify the vendor ID. So you will +find the driver name "ftdi_sio" in this file. +If your hardware not listen in this file. Look into source and search your +numbers in source. +More read file:/Documentation/usb/proc_usb_info.txt + +Drivers are all under contructions. So some drivers make a kernel panic. In +this case read all about the drivers dokumentaiona and the drivers source. +Some drivers need a other kernel driver, but not strictly checked in kenel +configuration. Here can help the ksymsoops. + + + +Install a Floppy (NEC UF0001) or USB Stick Fujitsu/Siemens/iomega: +------------------------------ +Copy files to target (FTP) and load all drivers. +Load Generic USB-Handler + insmod usbcore.o +Load USB-Host controller, parameters are optional (default urb_debug=0 io=220 irq=12) + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load drivers for disk storage and file systems + insmod scsi_mod.o + insmod usb-storage.o + insmod fat.o + insmod vfat.o + insmod sd_mod.o +Create node for floppy + mknod /dev/sda b 8 0 + +Put a disk into your floppy anth than plugin a USB-Floppy (such NEC Model UF0001) +into first USB-Port. Some Messages will be list on console or in file /proc/kmsg. +The disk is power on and the SCSI driver will search some partions on disk. Floppy +have no partions, so must use the first SCSI device without a partion number for mount. +Than mount the floppy: + mount /dev/sda /mnt -t vfat + +If you see a partions check with valid partion 1, you should mount this partion. +Mostly Memory Sticks are formated with one partion. But if Windows format it again, +no partions is use. + +You see that: + Partition check: + sda: sda1 +Than mount with follow steps: + mknod /dev/sda1 b 8 1 + mount /dev/sda1 /mnt -t vfat + +Create complete list of nodes for SCSI-devices: + # First inserted device + echo -n "Create /dev/sda... " + mknod /dev/sda b 8 0 + for i in 1 2 3 4 5 6 7 + do + echo -n "$i " + mknod sda$i c 8 $i + done + # Second inserted device + echo -n "Create /dev/sdb... " + mknod /dev/sdb b 8 16 + mknod /dev/sdb1 b 8 17 + echo " done" + # Set some rights + chown root.disk sd* + chmod 660 sd* + + +Install a Keyboard: +------------------- +Copy files to target (FTP) and load all drivers. +Load Generic USB-Handler + insmod usbcore.o +Load USB-Host controller, parameters are optional (default urb_debug=0 io=220 irq=12) + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load drivers for USB-Keyboard + insmod input.o + insmod keybdev.o + insmod usbkbd.o +Now you can plugin Keyboard into first USB-Port and login on first console. + +Something stuff: +"Undefined Symbols handle_scancode, keyboard_tasklet, kbd_ledfunc" at install ??? +USB keyboard need PC-style keyboard driver, because the USB driver +simulate standard AT-Keycodes. A normaly AT- or PS/2-Keyboard must not +exist for this. The driver says normaly error (Timeout) on boot. +You must enable CONFIG_VT in kernel konfiguration! +Character devices ---> + [*] Virtual terminal + [ ] Support for console on virtual terminal + +Read + + +Install a Mouse: +---------------- +Load Generic USB-Handle + insmod usbcore.o +Load USB-Host controller + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load Generic Input device + insmod input.o +Load USB-Mouse driver + insmod input.o + insmod mousedev.o + insmod usbmouse.o + +Read + + +Install a serial adapter (Sample FTDI): +--------------------------------------- +Load Generic USB-Handle + insmod usbcore.o +Load USB-Host controller + insmod hc_sl811.o +Run the USB-Filesystem + mount -t usbdevfs usbdevfs /proc/bus/usb +Load Generic derial device and hardware specific device + insmod usbserial.o + insmod ftdi_sio.o +Create node entry for this device + mknod /dev/ttyUSB0 c 188 0 +Than plugin the hardware into first USB port and +use serial device on /dev/ttyUSB0, such call a login: + /sbin/getty 115200 ttyUSB0 vt100 & + + +USB-Utils: +---------- +- usb-0.6-7.rpm, usb-0.6-7.src.rpm + /usr/sbin/lsusb, /usr/share/usb.ids + Good tool to list devices parameters. + You must load usbcore.o, hc_sl811.o and proc-usb before + program works right (use script usb.sh). + More details: Install usb-0.6-7.rpm on Your desktop and use "man lsusb". + + +Known Bugs: +----------- + +PL2302 Profilic USB to serial converter will not work with hc_sl811.c (Bulk/Timeout). +USB Floppy will not work with alternate driver sl811.o (Sector not found) + + +CHANGELOG: +---------- +* Fri 03 Okt 2003 hne +- Patch for 2.4.23-pre6 +- Only low level port io in hardware include as inline functions. +- Move hc_sl811 and sl811 into host directory. +- sl811 for two controllers (alternate x86 only). + +* Mit 24 Sep 2003 hne +- Misplaced "host/uhci.o" in Makefile. +- Move all x86/arm arch depens from main sl811.c to sl811-hw.h. + +* Die 23 Sep 2003 hne +- Put arm and x86 architectur into separate file in include directory. +- Modifications for both controllers on CF/USB1, alternate sl811 only. + Parameter for CF/USB1: "io=0x1f0,0x3f6 irq=14". + +* Fri 19 Sep 2003 hne +- First version for both controllers on USB1-104. +- Alternative driver sl811.c from kernel 2.4.22 (thanks Yinah), + also for 2.4.20. USB Sticks works, Floppy not. + +* Die 02 Sep 2003 hne +- IO range only 2 address. For CF1USB we need io addres 3F6 and 3F7, + but do not use 3f8 (ttyS0). + +* Mon 11 Aug 2003 hne +- Comments for using iomega Memory Stick + +* Don 12 Jun 2003 hne +- Added Bus-Name for Kernel 2.4.20, no pattern_test at unload driver. +- more doc + +* Fri May 16 2003 hne +- More comments, new patchfile, include usb-konfiguration as file + +* Wed May 14 2003 hne +- Patch error: Old Sources was in Kerneltree! + +* Mon Mar 17 2003 hne +- Copy usb SL811 from 2.4.19-rc into 2.4.20 kerneltree +- Add SL811 in Config and Make + +* 18.11.2002 hne +- hc_sl811_rh.c: + rh_unlink_urb: Use usb_dec_dev_use instand of usb_put_dev. Function + usb_put_dev isn't known in this module. Some others have a macro for + this. What is right usb_put_dev or usb_dec_dev_use? +- hc_sl811.c: + Split into 3 files. Arcitectures store in hc_sl811-arm.c and hc_sl811-x86.c + Correct release_region() for both io address, so we can unload modul and + load again without reboot. + All IO access use 8 bit Data and register number (type __u8). + All functions static. + Only x86: base_addr renamed to io. data_reg_addr not used. diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Fri Oct 24 17:01:14 2003 +++ b/drivers/usb/Makefile Fri Oct 24 17:01:14 2003 @@ -77,6 +77,9 @@ obj-y += host/usb-ohci.o endif +subdir-$(CONFIG_USB_SL811HS_ALT) += host +subdir-$(CONFIG_USB_SL811HS) += host + obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_HID) += hid.o obj-$(CONFIG_USB_KBD) += usbkbd.o diff -Nru a/drivers/usb/hc_simple.c b/drivers/usb/hc_simple.c --- a/drivers/usb/hc_simple.c Fri Oct 24 17:01:14 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1072 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) - * for embedded HCs (SL811HS) - * - * USB URB handling, hci_ hcs_ - * URB queueing, qu_ - * Transfer scheduling, sh_ - * - * - *-------------------------------------------------------------------------* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -/* main lock for urb access */ -static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------*/ -/* URB HCD API function layer - * * * */ - -/*************************************************************************** - * Function Name : hcs_urb_queue - * - * This function initializes the urb status and length before queueing the - * urb. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 - **************************************************************************/ -static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) -{ - int i; - - DBGFUNC ("enter hcs_urb_queue\n"); - if (usb_pipeisoc (urb->pipe)) { - DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); - for (i = 0; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EXDEV; - } - - /* urb->next hack : 1 .. resub, 0 .. single shot */ - /* urb->interval = urb->next ? 1 : 0; */ - } - - urb->status = -EINPROGRESS; - urb->actual_length = 0; - urb->error_count = 0; - - if (usb_pipecontrol (urb->pipe)) - hc_flush_data_cache (hci, urb->setup_packet, 8); - if (usb_pipeout (urb->pipe)) - hc_flush_data_cache (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - - qu_queue_urb (hci, urb); - - return 0; -} - -/*************************************************************************** - * Function Name : hcs_return_urb - * - * This function the return path of URB back to the USB core. It calls the - * the urb complete function if exist, and also handles the resubmition of - * interrupt URBs. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit - * - * Return: 0 - **************************************************************************/ -static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct usb_device *dev = urb->dev; - int resubmit = 0; - - DBGFUNC ("enter hcs_return_urb, urb pointer = 0x%x, " - "transferbuffer point = 0x%x, " - " setup packet pointer = 0x%x, context pointer = 0x%x \n", - (__u32 *) urb, (__u32 *) urb->transfer_buffer, - (__u32 *) urb->setup_packet, (__u32 *) urb->context); - if (urb_debug) - urb_print (urb, "RET", usb_pipeout (urb->pipe)); - - resubmit = urb->interval && resub_ok; - - urb->dev = urb->hcpriv = NULL; - - if (urb->complete) { - urb->complete (urb); /* call complete */ - } - - if (resubmit) { - /* requeue the URB */ - urb->dev = dev; - hcs_urb_queue (hci, urb); - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_submit_urb - * - * This function is called by the USB core API when an URB is available to - * process. This function does the following - * - * 1) Check the validity of the URB - * 2) Parse the device number from the URB - * 3) Pass the URB to the root hub routine if its intended for the hub, else - * queue the urb for the attached device. - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_submit_urb (struct urb * urb) -{ - hci_t *hci; - unsigned int pipe = urb->pipe; - unsigned long flags; - int ret; - - DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); - if (!urb->dev || !urb->dev->bus || urb->hcpriv) - return -EINVAL; - - if (usb_endpoint_halted - (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) { - printk ("hci_submit_urb: endpoint_halted\n"); - return -EPIPE; - } - hci = (hci_t *) urb->dev->bus->hcpriv; - - /* a request to the virtual root hub */ - - if (usb_pipedevice (pipe) == hci->rh.devnum) { - if (urb_debug > 1) - urb_print (urb, "SUB-RH", usb_pipein (pipe)); - - return rh_submit_urb (urb); - } - - /* queue the URB to its endpoint-queue */ - - spin_lock_irqsave (&usb_urb_lock, flags); - ret = hcs_urb_queue (hci, urb); - if (ret != 0) { - /* error on return */ - DBGERR - ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", - ret, urb->status); - } - - spin_unlock_irqrestore (&usb_urb_lock, flags); - - return ret; - -} - -/*************************************************************************** - * Function Name : hci_unlink_urb - * - * This function mark the URB to unlink - * - * Input: urb = USB request block data structure - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_unlink_urb (struct urb * urb) -{ - unsigned long flags; - hci_t *hci; - DECLARE_WAITQUEUE (wait, current); - void *comp = NULL; - - DBGFUNC ("enter hci_unlink_urb\n"); - - if (!urb) /* just to be sure */ - return -EINVAL; - - if (!urb->dev || !urb->dev->bus) - return -ENODEV; - - hci = (hci_t *) urb->dev->bus->hcpriv; - - /* a request to the virtual root hub */ - if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { - return rh_unlink_urb (urb); - } - - if (urb_debug) - urb_print (urb, "UNLINK", 1); - - spin_lock_irqsave (&usb_urb_lock, flags); - - if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { - /* URB active? */ - - if (urb-> - transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { - /* asynchron with callback */ - - list_del (&urb->urb_list); /* relink the urb to the del list */ - list_add (&urb->urb_list, &hci->del_list); - spin_unlock_irqrestore (&usb_urb_lock, flags); - - } else { - /* synchron without callback */ - - add_wait_queue (&hci->waitq, &wait); - - set_current_state (TASK_UNINTERRUPTIBLE); - comp = urb->complete; - urb->complete = NULL; - - list_del (&urb->urb_list); /* relink the urb to the del list */ - list_add (&urb->urb_list, &hci->del_list); - - spin_unlock_irqrestore (&usb_urb_lock, flags); - - schedule_timeout (HZ / 50); - - if (!list_empty (&urb->urb_list)) - list_del (&urb->urb_list); - - urb->complete = comp; - urb->hcpriv = NULL; - remove_wait_queue (&hci->waitq, &wait); - } - } else { - /* hcd does not own URB but we keep the driver happy anyway */ - spin_unlock_irqrestore (&usb_urb_lock, flags); - - if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { - urb->status = -ENOENT; - urb->actual_length = 0; - urb->complete (urb); - urb->status = 0; - } else { - urb->status = -ENOENT; - } - } - - return 0; -} - -/*************************************************************************** - * Function Name : hci_alloc_dev - * - * This function allocates private data space for the usb device and - * initialize the endpoint descriptor heads. - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 if success or error code - **************************************************************************/ -static int hci_alloc_dev (struct usb_device *usb_dev) -{ - struct hci_device *dev; - int i; - - DBGFUNC ("enter hci_alloc_dev\n"); - dev = kmalloc (sizeof (*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - memset (dev, 0, sizeof (*dev)); - - for (i = 0; i < 32; i++) { - INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); - dev->ed[i].pipe_head = NULL; - } - - usb_dev->hcpriv = dev; - - DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); - - return 0; - -} - -/*************************************************************************** - * Function Name : hci_free_dev - * - * This function de-allocates private data space for the usb devic - * - * Input: usb_dev = pointer to the usb device - * - * Return: 0 - **************************************************************************/ -static int hci_free_dev (struct usb_device *usb_dev) -{ - DBGFUNC ("enter hci_free_dev\n"); - - if (usb_dev->hcpriv) - kfree (usb_dev->hcpriv); - - usb_dev->hcpriv = NULL; - - return 0; -} - -/*************************************************************************** - * Function Name : hci_get_current_frame_number - * - * This function get the current USB frame number - * - * Input: usb_dev = pointer to the usb device - * - * Return: frame number - **************************************************************************/ -static int hci_get_current_frame_number (struct usb_device *usb_dev) -{ - hci_t *hci = usb_dev->bus->hcpriv; - DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", - hci->frame_number); - - return (hci->frame_number); -} - -/*************************************************************************** - * List of all io-functions - **************************************************************************/ - -static struct usb_operations hci_device_operations = { - allocate: hci_alloc_dev, - deallocate: hci_free_dev, - get_frame_number: hci_get_current_frame_number, - submit_urb: hci_submit_urb, - unlink_urb: hci_unlink_urb, -}; - -/*************************************************************************** - * URB queueing: - * - * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of - * active URBs. - * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) - * For every endpoint the head URB of the queued URBs is linked to one of - * those lists. - * - * The rest of the queued URBs of an endpoint are linked into a - * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) - * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is - * in one of the active URB lists. - * - * The index of an endpoint consists of its number and its direction. - * - * The state of an intr and iso URB is 0. - * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK - * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) - * - **************************************************************************/ - -/*************************************************************************** - * Function Name : qu_urb_timeout - * - * This function is called when the URB timeout. The function unlinks the - * URB. - * - * Input: lurb: URB - * - * Return: none - **************************************************************************/ -#ifdef HC_URB_TIMEOUT -static void qu_urb_timeout (unsigned long lurb) -{ - struct urb *urb = (struct urb *) lurb; - - DBGFUNC ("enter qu_urb_timeout\n"); - urb->transfer_flags |= USB_TIMEOUT_KILLED; - hci_unlink_urb (urb); -} -#endif - -/*************************************************************************** - * Function Name : qu_pipeindex - * - * This function gets the index of the pipe. - * - * Input: pipe: the urb pipe - * - * Return: index - **************************************************************************/ -static inline int qu_pipeindex (__u32 pipe) -{ - DBGFUNC ("enter qu_pipeindex\n"); - return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); -} - -/*************************************************************************** - * Function Name : qu_seturbstate - * - * This function set the state of the URB. - * - * control pipe: 3 states -- Setup, data, status - * interrupt and bulk pipe: 1 state -- data - * - * Input: urb = USB request block data structure - * state = the urb state - * - * Return: none - **************************************************************************/ -static inline void qu_seturbstate (struct urb * urb, int state) -{ - DBGFUNC ("enter qu_seturbstate\n"); - urb->pipe &= ~0x1f; - urb->pipe |= state & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_urbstate - * - * This function get the current state of the URB. - * - * Input: urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static inline int qu_urbstate (struct urb * urb) -{ - - DBGFUNC ("enter qu_urbstate\n"); - - return urb->pipe & 0x1f; -} - -/*************************************************************************** - * Function Name : qu_queue_active_urb - * - * This function adds the urb to the appropriate active urb list and set - * the urb state. - * - * There are four active lists: isochoronous list, interrupt list, - * control list, and bulk list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * ed = endpoint descriptor - * - * Return: none - **************************************************************************/ -static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) -{ - int urb_state = 0; - DBGFUNC ("enter qu_queue_active_urb\n"); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - list_add (&urb->urb_list, &hci->ctrl_list); - urb_state = US_CTRL_SETUP; - break; - - case PIPE_BULK: - list_add (&urb->urb_list, &hci->bulk_list); - if ((urb->transfer_flags & USB_ZERO_PACKET) - && urb->transfer_buffer_length > 0 - && - ((urb->transfer_buffer_length % - usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe))) == 0)) { - urb_state = US_BULK0; - } - break; - - case PIPE_INTERRUPT: - urb->start_frame = hci->frame_number; - list_add (&urb->urb_list, &hci->intr_list); - break; - - case PIPE_ISOCHRONOUS: - list_add (&urb->urb_list, &hci->iso_list); - break; - } - -#ifdef HC_URB_TIMEOUT - if (urb->timeout) { - ed->timeout.data = (unsigned long) urb; - ed->timeout.expires = urb->timeout + jiffies; - ed->timeout.function = qu_urb_timeout; - add_timer (&ed->timeout); - } -#endif - - qu_seturbstate (urb, urb_state); -} - -/*************************************************************************** - * Function Name : qu_queue_urb - * - * This function adds the urb to the endpoint descriptor list - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: none - **************************************************************************/ -static int qu_queue_urb (hci_t * hci, struct urb * urb) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("Enter qu_queue_urb\n"); - - /* for ISOC transfers calculate start frame index */ - - if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) { - urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; - } - - if (ed->pipe_head) { - __list_add (&urb->urb_list, ed->urb_queue.prev, - &(ed->urb_queue)); - } else { - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - if (++hci->active_urbs == 1) - hc_start_int (hci); - } - - return 0; -} - -/*************************************************************************** - * Function Name : qu_next_urb - * - * This function removes the URB from the queue and add the next URB to - * active list. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct hci_device *hci_dev = usb_to_hci (urb->dev); - epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; - - DBGFUNC ("enter qu_next_urb\n"); - list_del (&urb->urb_list); - INIT_LIST_HEAD (&urb->urb_list); - if (ed->pipe_head == urb) { - -#ifdef HC_URB_TIMEOUT - if (urb->timeout) - del_timer (&ed->timeout); -#endif - - if (!--hci->active_urbs) - hc_stop_int (hci); - - if (!list_empty (&ed->urb_queue)) { - urb = list_entry (ed->urb_queue.next, struct urb, urb_list); - list_del (&urb->urb_list); - INIT_LIST_HEAD (&urb->urb_list); - ed->pipe_head = urb; - qu_queue_active_urb (hci, urb, ed); - } else { - ed->pipe_head = NULL; - urb = NULL; - } - } - return urb; -} - -/*************************************************************************** - * Function Name : qu_return_urb - * - * This function is part of the return path. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * resub_ok = resubmit flag - * - * Return: pointer to the next urb - **************************************************************************/ -static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) -{ - struct urb *next_urb; - - DBGFUNC ("enter qu_return_rub\n"); - next_urb = qu_next_urb (hci, urb, resub_ok); - hcs_return_urb (hci, urb, resub_ok); - return next_urb; -} - -/*************************************************************************** - * Function Name : sh_scan_iso_urb_list - * - * This function goes throught the isochronous urb list and schedule the - * the transfer. - * - * Note: This function has not tested yet - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * frame_number = the frame number - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, - int frame_number) -{ - struct list_head *lh = list_lh->next; - struct urb *urb; - - DBGFUNC ("enter sh_scan_iso_urb_list\n"); - hci->td_array->len = 0; - - while (lh != list_lh) { - urb = list_entry (lh, struct urb, urb_list); - lh = lh->next; - if (((frame_number - urb->start_frame) & 0x7ff) < - urb->number_of_packets) { - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - if (((frame_number - - urb->start_frame) & 0x7ff) > 0x400) { - if (qu_urbstate (urb) > 0) - urb = qu_return_urb (hci, urb, 1); - else - urb = qu_next_urb (hci, urb, 1); - - if (lh == list_lh && urb) - lh = &urb->urb_list; - } - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_scan_urb_list - * - * This function goes through the urb list and schedule the - * the transaction. - * - * Input: hci = data structure for the host controller - * list_lh = pointer to the isochronous list - * - * Return: 0 = unsuccessful; 1 = successful - **************************************************************************/ -static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) -{ - struct list_head *lh = NULL; - struct urb *urb; - - if (list_lh == NULL) { - DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); - } - - DBGFUNC ("enter sh_scan_urb_list: frame# \n"); - - list_for_each (lh, list_lh) { - urb = list_entry (lh, struct urb, urb_list); - if (urb == NULL) - return 1; - if (!usb_pipeint (urb->pipe) - || (((hci->frame_number - urb->start_frame) - & 0x7ff) >= urb->interval)) { - DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, urb->interval, - usb_pipeint (urb->pipe)); - if (!sh_add_packet (hci, urb)) { - return 0; - } else { - DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", - urb->start_frame, hci->frame_number, - urb->interval, usb_pipeint (urb->pipe)); - urb->start_frame = hci->frame_number; - return 0; - - } - } - } - return 1; -} - -/*************************************************************************** - * Function Name : sh_shedule_trans - * - * This function schedule the USB transaction. - * This function will process the endpoint in the following order: - * interrupt, control, and bulk. - * - * Input: hci = data structure for the host controller - * isSOF = flag indicate if Start Of Frame has occurred - * - * Return: 0 - **************************************************************************/ -static int sh_schedule_trans (hci_t * hci, int isSOF) -{ - int units_left = 1; - struct list_head *lh; - - if (hci == NULL) { - DBGERR ("sh_schedule_trans: hci == NULL\n"); - return 0; - } - if (hci->td_array == NULL) { - DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); - return 0; - } - - if (hci->td_array->len != 0) { - DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - } - - /* schedule the next available interrupt transfer or the next - * stage of the interrupt transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { - units_left = sh_scan_urb_list (hci, &hci->intr_list); - } - - /* schedule the next available control transfer or the next - * stage of the control transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { - units_left = sh_scan_urb_list (hci, &hci->ctrl_list); - } - - /* schedule the next available bulk transfer or the next - * stage of the bulk transfer */ - - if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { - sh_scan_urb_list (hci, &hci->bulk_list); - - /* be fair to each BULK URB (move list head around) - * only when the new SOF happens */ - - lh = hci->bulk_list.next; - list_del (&hci->bulk_list); - list_add (&hci->bulk_list, lh); - } - return 0; -} - -/*************************************************************************** - * Function Name : sh_add_packet - * - * This function forms the packet and transmit the packet. This function - * will handle all endpoint type: isochoronus, interrupt, control, and - * bulk. - * - * Input: hci = data structure for the host controller - * urb = USB request block data structure - * - * Return: 0 = unsucessful; 1 = successful - **************************************************************************/ -static int sh_add_packet (hci_t * hci, struct urb * urb) -{ - __u8 *data = NULL; - int len = 0; - int toggle = 0; - int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - int endpoint = usb_pipeendpoint (urb->pipe); - int address = usb_pipedevice (urb->pipe); - int slow = (((urb->pipe) >> 26) & 1); - int out = usb_pipeout (urb->pipe); - int pid = 0; - int ret; - int i = 0; - int iso = 0; - - DBGFUNC ("enter sh_add_packet\n"); - if (maxps == 0) - maxps = 8; - - /* calculate len, toggle bit and add the transaction */ - switch (usb_pipetype (urb->pipe)) { - case PIPE_ISOCHRONOUS: - pid = out ? PID_OUT : PID_IN; - iso = 1; - i = hci->frame_number - urb->start_frame; - data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - len = urb->iso_frame_desc[i].length; - break; - - case PIPE_BULK: /* BULK and BULK0 */ - case PIPE_INTERRUPT: - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, endpoint, out); - break; - - case PIPE_CONTROL: - switch (qu_urbstate (urb)) { - case US_CTRL_SETUP: - len = 8; - pid = PID_SETUP; - data = urb->setup_packet; - toggle = 0; - break; - - case US_CTRL_DATA: - if (!hci->last_packet_nak) { - /* The last packet received is not a nak: - * reset the nak count - */ - - hci->nakCnt = 0; - } - if (urb->transfer_buffer_length != 0) { - pid = out ? PID_OUT : PID_IN; - len = urb->transfer_buffer_length - urb->actual_length; - data = urb->transfer_buffer + urb->actual_length; - toggle = (urb->actual_length & maxps) ? 0 : 1; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } else { - /* correct state and fall through */ - qu_seturbstate (urb, US_CTRL_ACK); - } - - case US_CTRL_ACK: - len = 0; - - /* reply in opposite direction */ - pid = !out ? PID_OUT : PID_IN; - toggle = 1; - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - break; - } - } - - ret = - hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, - address, pid, iso, qu_urbstate (urb)); - - DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " - "max:%d\n len:%d ret:%d data:%p left:%d\n", - address, endpoint, pid, toggle, iso, slow, - maxps, len, ret, data, hci->hp.units_left); - - if (ret >= 0) { - hci->td_array->td[hci->td_array->len].urb = urb; - hci->td_array->td[hci->td_array->len].len = ret; - hci->td_array->td[hci->td_array->len].iso_index = i; - hci->td_array->len++; - hci->active_trans = 1; - return 1; - } - return 0; -} - -/*************************************************************************** - * Function Name : cc_to_error - * - * This function maps the SL811HS hardware error code to the linux USB error - * code. - * - * Input: cc = hardware error code - * - * Return: USB error code - **************************************************************************/ -static int cc_to_error (int cc) -{ - int errCode = 0; - if (cc & SL11H_STATMASK_ERROR) { - errCode |= -EILSEQ; - } else if (cc & SL11H_STATMASK_OVF) { - errCode |= -EOVERFLOW; - } else if (cc & SL11H_STATMASK_STALL) { - errCode |= -EPIPE; - } - return errCode; -} - -/*************************************************************************** - * Function Name : sh_done_list - * - * This function process the packet when it has done finish transfer. - * - * 1) It handles hardware error - * 2) It updates the URB state - * 3) If the USB transaction is complete, it start the return stack path. - * - * Input: hci = data structure for the host controller - * isExcessNak = flag tells if there excess NAK condition occurred - * - * Return: urb_state or -1 if the transaction has complete - **************************************************************************/ -static int sh_done_list (hci_t * hci, int *isExcessNak) -{ - int actbytes = 0; - int active = 0; - void *data = NULL; - int cc; - int maxps; - int toggle; - struct urb *urb; - int urb_state = 0; - int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ - int trans = 0; - int len; - int iso_index = 0; - int out; - int pid = 0; - int debugLen = 0; - - *isExcessNak = 0; - - DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", - hci->td_array->len); - - debugLen = hci->td_array->len; - if (debugLen > 1) - DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", - hci->td_array->len); - - for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; - trans++) { - urb = hci->td_array->td[trans].urb; - len = hci->td_array->td[trans].len; - out = usb_pipeout (urb->pipe); - - if (usb_pipeisoc (urb->pipe)) { - iso_index = hci->td_array->td[trans].iso_index; - data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; - toggle = 0; - } else { - data = urb->transfer_buffer + urb->actual_length; - toggle = usb_gettoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)); - - } - urb_state = qu_urbstate (urb); - pid = out ? PID_OUT : PID_IN; - ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, - pid, urb_state); - maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); - - if (maxps == 0) - maxps = 8; - - active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); - - /* If the transfer is not bulk in, then it is necessary to get all - * data specify by the urb->transfer_len. - */ - - if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) - active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); - - if (urb->transfer_buffer_length == urb->actual_length + actbytes) - active = 0; - - if ((cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | - SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) - && !(cc & SL11H_STATMASK_NAK)) { - if (++urb->error_count > 3) { - DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", - urb->error_count, cc); - urb_state = 0; - active = 0; - } else { - DBGERR ("done_list: packet err, cc = 0x%x, " - " urb->length = 0x%x, actual_len = 0x%x," - " urb_state =0x%x\n", - cc, urb->transfer_buffer_length, - urb->actual_length, urb_state); -// if (cc & SL11H_STATMASK_STALL) { - /* The USB function is STALLED on a control pipe (0), - * then it needs to send the SETUP command again to - * clear the STALL condition - */ - -// if (usb_pipeendpoint (urb->pipe) == 0) { -// urb_state = 2; -// active = 0; -// } -// } else - active = 1; - } - } else { - if (cc & SL11H_STATMASK_NAK) { - if (hci->nakCnt < 0x10000) { - hci->nakCnt++; - hci->last_packet_nak = 1; - active = 1; - *isExcessNak = 0; - } else { - DBGERR ("done_list: nak count exceed limit\n"); - active = 0; - *isExcessNak = 1; - hci->nakCnt = 0; - } - } else { - hci->nakCnt = 0; - hci->last_packet_nak = 0; - } - - if (urb_state != US_CTRL_SETUP) { - /* no error */ - urb->actual_length += actbytes; - usb_settoggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), toggle); - } - if (usb_pipeisoc (urb->pipe)) { - urb->iso_frame_desc[iso_index].actual_length = actbytes; - urb->iso_frame_desc[iso_index].status = cc_to_error (cc); - active = (iso_index < urb->number_of_packets); - } - } - if (!active) { - if (!urb_state) { - urb->status = cc_to_error (cc); - if (urb->status) { - DBGERR ("error on received packet: urb->status = 0x%x\n", - urb->status); - } - hci->td_array->len = 0; - qu_return_urb (hci, urb, 1); - return -1; - } else { - /* We do not want to decrement the urb_state if exceeded nak, - * because we need to finish the data stage of the control - * packet - */ - - if (!(*isExcessNak)) - urb_state--; - qu_seturbstate (urb, urb_state); - } - } - } - - if (urb_state < 0) - DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", - urb_state); - if (debugLen != hci->td_array->len) { - DBGERR ("ERROR: done_list, debugLen!= td_array->len," - "debugLen = 0x%x, hci->td_array->len = 0x%x\n", - debugLen, hci->td_array->len); - } - - hci->td_array->len = 0; - - return urb_state; -} diff -Nru a/drivers/usb/hc_simple.h b/drivers/usb/hc_simple.h --- a/drivers/usb/hc_simple.h Fri Oct 24 17:01:14 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,231 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/* list of all controllers using this driver - * */ - -static LIST_HEAD (hci_hcd_list); - -/* URB states (urb_state) */ -/* isoc, interrupt single state */ - -/* bulk transfer main state and 0-length packet */ -#define US_BULK 0 -#define US_BULK0 1 -/* three setup states */ -#define US_CTRL_SETUP 2 -#define US_CTRL_DATA 1 -#define US_CTRL_ACK 0 - -/*-------------------------------------------------------------------------*/ -/* HC private part of a device descriptor - * */ - -#define NUM_EDS 32 - -typedef struct epd { - struct urb *pipe_head; - struct list_head urb_queue; -// int urb_state; - struct timer_list timeout; - int last_iso; /* timestamp of last queued ISOC transfer */ - -} epd_t; - -struct hci_device { - epd_t ed[NUM_EDS]; -}; - -/*-------------------------------------------------------------------------*/ -/* Virtual Root HUB - */ - -#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) - -struct virt_root_hub { - int devnum; /* Address of Root Hub endpoint */ - void *urb; /* interrupt URB of root hub */ - int send; /* active flag */ - int interval; /* intervall of roothub interrupt transfers */ - struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ -}; - -#if 1 -/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 - -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -#endif - -/*-------------------------------------------------------------------------*/ -/* struct for each HC - */ - -#define MAX_TRANS 32 - -typedef struct td { - struct urb *urb; - __u16 len; - __u16 iso_index; -} td_t; - -typedef struct td_array { - int len; - td_t td[MAX_TRANS]; -} td_array_t; - -typedef struct hci { - struct virt_root_hub rh; /* roothub */ - wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ - int active; /* HC is operating */ - - struct list_head ctrl_list; /* set of ctrl endpoints */ - struct list_head bulk_list; /* set of bulk endpoints */ - struct list_head iso_list; /* set of isoc endpoints */ - struct list_head intr_list; /* ordered (tree) set of int endpoints */ - struct list_head del_list; /* set of entpoints to be deleted */ - - td_array_t *td_array; - td_array_t a_td_array; - td_array_t i_td_array[2]; - - struct list_head hci_hcd_list; /* list of all hci_hcd */ - struct usb_bus *bus; /* our bus */ - -// int trans; /* number of transactions pending */ - int active_urbs; - int active_trans; - int frame_number; /* frame number */ - hcipriv_t hp; /* individual part of hc type */ - int nakCnt; - int last_packet_nak; - -} hci_t; - -/*-------------------------------------------------------------------------*/ -/* condition (error) CC codes and mapping OHCI like - */ - -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - -/* urb interface functions */ -static int hci_get_current_frame_number (struct usb_device *usb_dev); -static int hci_unlink_urb (struct urb * urb); - -static int qu_queue_urb (hci_t * hci, struct urb * urb); - -/* root hub */ -static int rh_init_int_timer (struct urb * urb); -static int rh_submit_urb (struct urb * urb); -static int rh_unlink_urb (struct urb * urb); - -/* schedule functions */ -static int sh_add_packet (hci_t * hci, struct urb * urb); - -/* hc specific functions */ -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state); -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state); - -static void hc_start_int (hci_t * hci); -static void hc_stop_int (hci_t * hci); -static void SL811Write (hci_t * hci, char offset, char data); - -/* debug| print the main components of an URB - * small: 0) header + data packets 1) just header */ - -static void urb_print (struct urb * urb, char *str, int small) -{ - unsigned int pipe = urb->pipe; - int i, len; - - if (!urb->dev || !urb->dev->bus) { - dbg ("%s URB: no dev", str); - return; - } - - printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", - str, hci_get_current_frame_number (urb->dev), - usb_pipedevice (pipe), usb_pipeendpoint (pipe), - usb_pipeout (pipe) ? 'O' : 'I', - usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") - : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, - urb->actual_length, urb->transfer_buffer_length, urb->status, - urb->status); - if (!small) { - if (usb_pipecontrol (pipe)) { - printk (__FILE__ ": cmd(8):"); - for (i = 0; i < 8; i++) - printk (" %02x", ((__u8 *) urb->setup_packet)[i]); - printk ("\n"); - } - if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { - printk (__FILE__ ": data(%d/%d):", urb->actual_length, - urb->transfer_buffer_length); - len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; - for (i = 0; i < 2096 && i < len; i++) - printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); - printk ("%s stat:%d\n", i < len ? "..." : "", - urb->status); - } - } -} diff -Nru a/drivers/usb/hc_sl811.c b/drivers/usb/hc_sl811.c --- a/drivers/usb/hc_sl811.c Fri Oct 24 17:01:14 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,1359 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) - * - * requires (includes) hc_simple.[hc] simple generic HCD frontend - * - * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC. - * - *-------------------------------------------------------------------------* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "../core/hcd.h" - -#undef HC_URB_TIMEOUT -#undef HC_SWITCH_INT -#undef HC_ENABLE_ISOC - -#define SL811_DEBUG_ERR - -#ifdef SL811_DEBUG_ERR -#define DBGERR(fmt, args...) printk(fmt,## args) -#else -#define DBGERR(fmt, args...) -#endif - -#ifdef SL811_DEBUG -#define DBG(fmt, args...) printk(fmt,## args) -#else -#define DBG(fmt, args...) -#endif - -#ifdef SL811_DEBUG_FUNC -#define DBGFUNC(fmt, args...) printk(fmt,## args) -#else -#define DBGFUNC(fmt, args...) -#endif - -#ifdef SL811_DEBUG_DATA -#define DBGDATAR(fmt, args...) printk(fmt,## args) -#define DBGDATAW(fmt, args...) printk(fmt,## args) -#else -#define DBGDATAR(fmt, args...) -#define DBGDATAW(fmt, args...) -#endif - -#ifdef SL811_DEBUG_VERBOSE -#define DBGVERBOSE(fmt, args...) printk(fmt,## args) -#else -#define DBGVERBOSE(fmt, args...) -#endif - -#define TRUE 1 -#define FALSE 0 - -#define HC_SWITCH_INT -#include "hc_sl811.h" -#include "hc_simple.h" - -static int urb_debug = 0; - -#include "hc_simple.c" -#include "hc_sl811_rh.c" - -/* The base_addr, data_reg_addr, and irq number are board specific. - * The current values are design to run on the Accelent SA1110 IDP - * NOTE: values need to modify for different development boards - */ - -static int base_addr = 0xd3800000; -static int data_reg_addr = 0xd3810000; -static int irq = 34; - -/* forware declaration */ - -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state); - -static int sofWaitCnt = 0; - -MODULE_PARM (urb_debug, "i"); -MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); - -MODULE_PARM (base_addr, "i"); -MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); -MODULE_PARM (data_reg_addr, "i"); -MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); -MODULE_PARM (irq, "i"); -MODULE_PARM_DESC (irq, "IRQ 34 (default)"); - -static int hc_reset (hci_t * hci); - -/*************************************************************************** - * Function Name : SL811Read - * - * Read a byte of data from the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * - * Return: data - **************************************************************************/ -char SL811Read (hci_t * hci, char offset) -{ - hcipriv_t *hp = &hci->hp; - char data; - writeb (offset, hp->hcport); - wmb (); - data = readb (hp->hcport2); - rmb (); - return (data); -} - -/*************************************************************************** - * Function Name : SL811Write - * - * Write a byte of data to the SL811H/SL11H - * - * Input: hci = data structure for the host controller - * offset = address of SL811/SL11H register or memory - * data = the data going to write to SL811H - * - * Return: none - **************************************************************************/ -void SL811Write (hci_t * hci, char offset, char data) -{ - hcipriv_t *hp = &hci->hp; - writeb (offset, hp->hcport); - writeb (data, hp->hcport2); - wmb (); -} - -/*************************************************************************** - * Function Name : SL811BufRead - * - * Read consecutive bytes of data from the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the buffer where the data will store - * size = number of bytes to read - * - * Return: none - **************************************************************************/ -void SL811BufRead (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAR ("SL811BufRead: offset = 0x%x, data = ", offset); - while (size--) { - *buf++ = (char) readb (hp->hcport2); - DBGDATAR ("0x%x ", *(buf - 1)); - rmb (); - } - DBGDATAR ("\n"); -} - -/*************************************************************************** - * Function Name : SL811BufWrite - * - * Write consecutive bytes of data to the SL811H/SL11H buffer - * - * Input: hci = data structure for the host controller - * offset = SL811/SL11H register offset - * buf = the data buffer - * size = number of bytes to write - * - * Return: none - **************************************************************************/ -void SL811BufWrite (hci_t * hci, short offset, char *buf, short size) -{ - hcipriv_t *hp = &hci->hp; - if (size <= 0) - return; - writeb ((char) offset, hp->hcport); - wmb (); - DBGDATAW ("SL811BufWrite: offset = 0x%x, data = ", offset); - while (size--) { - DBGDATAW ("0x%x ", *buf); - writeb (*buf, hp->hcport2); - wmb (); - buf++; - } - DBGDATAW ("\n"); -} - -/*************************************************************************** - * Function Name : regTest - * - * This routine test the Read/Write functionality of SL811HS registers - * - * 1) Store original register value into a buffer - * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) - * 3) Read from register - * 4) Compare the written value with the read value and make sure they are - * equivalent - * 5) Restore the original register value - * - * Input: hci = data structure for the host controller - * - * - * Return: TRUE = passed; FALSE = failed - **************************************************************************/ -int regTest (hci_t * hci) -{ - int i, data, result = TRUE; - char buf[256]; - - DBGFUNC ("Enter regTest\n"); - for (i = 0x10; i < 256; i++) { - /* save the original buffer */ - buf[i] = (char) SL811Read (hci, i); - - /* Write the new data to the buffer */ - SL811Write (hci, i, i); - } - - /* compare the written data */ - for (i = 0x10; i < 256; i++) { - data = SL811Read (hci, i); - if (data != i) { - DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", - data, i); - result = FALSE; - } - } - - /* restore the data */ - for (i = 0x10; i < 256; i++) { - SL811Write (hci, i, buf[i]); - } - - return (result); -} - -/*************************************************************************** - * Function Name : regShow - * - * Display all SL811HS register values - * - * Input: hci = data structure for the host controller - * - * Return: none - **************************************************************************/ -void regShow (hci_t * hci) -{ - int i; - for (i = 0; i < 256; i++) { - printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); - } -} - -/************************************************************************ - * Function Name : USBReset - * - * This function resets SL811HS controller and detects the speed of - * the connecting device - * - * Input: hci = data structure for the host controller - * - * Return: 0 = no device attached; 1 = USB device attached - * - ***********************************************************************/ -static int USBReset (hci_t * hci) -{ - int status; - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("enter USBReset\n"); - - SL811Write (hci, SL11H_CTLREG2, 0xae); - - // setup master and full speed - - SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB - mdelay (20); // 20ms - SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 - - for (status = 0; status < 100; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits - - status = SL811Read (hci, SL11H_INTSTATREG); - - if (status & 0x40) // Check if device is removed - { - DBG ("USBReset: Device removed\n"); - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - hp->RHportStatus->portStatus &= - ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); - - return 0; - } - - SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth - SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 - SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 - SL811Write (hci, SL11H_SOFLOWREG, 0xe0); - - if (!(status & 0x80)) { - /* slow speed device connect directly to root-hub */ - - DBG ("USBReset: low speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xee); - SL811Write (hci, SL11H_CTLREG1, 0x21); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= - (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); - - /* clear all interrupt bits */ - - for (status = 0; status < 20; status++) - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } else { - /* full speed device connect directly to root hub */ - - DBG ("USBReset: full speed Device attached\n"); - SL811Write (hci, SL11H_CTLREG1, 0x8); - mdelay (20); - SL811Write (hci, SL11H_SOFTMRREG, 0xae); - SL811Write (hci, SL11H_CTLREG1, 0x01); - - /* start the SOF or EOP */ - - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); - hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); - hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; - - /* clear all interrupt bits */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - } - - /* enable all interrupts */ - SL811Write (hci, SL11H_INTENBLREG, - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV); - - return 1; -} - -/*-------------------------------------------------------------------------*/ -/* tl functions */ -static inline void hc_mark_last_trans (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u8 *ptd = hp->tl; - - dbg ("enter hc_mark_last_trans\n"); - if (ptd == NULL) { - printk ("hc_mark_last_trans: ptd = null\n"); - return; - } - if (hp->xferPktLen > 0) - *(ptd + hp->tl_last) |= (1 << 3); -} - -static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) -{ -} - -/************************************************************************ - * Function Name : hc_add_trans - * - * This function sets up the SL811HS register and transmit the USB packets. - * - * 1) Determine if enough time within the current frame to send the packet - * 2) Load the data into the SL811HS register - * 3) Set the appropriate command to the register and trigger the transmit - * - * Input: hci = data structure for the host controller - * len = data length - * data = transmitting data - * toggle = USB toggle bit, either 0 or 1 - * maxps = maximum packet size for this endpoint - * slow = speed of the device - * endpoint = endpoint number - * address = USB address of the device - * pid = packet ID - * format = - * urb_state = the current stage of USB transaction - * - * Return: 0 = no time left to schedule the transfer - * 1 = success - * - ***********************************************************************/ -static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, - int maxps, int slow, int endpoint, int address, - int pid, int format, int urb_state) -{ - hcipriv_t *hp = &hci->hp; - __u16 speed; - int ii, jj, kk; - - DBGFUNC ("enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x," - " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, - i address, pid, format); - - if (len > maxps) { - len = maxps; - } - - speed = hp->RHportStatus->portStatus; - if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 1024; - } else { - if (slow) { -// ii = (8*7*8 + 6*3) * len + 800; - ii = 8 * 8 * len + 2048; - } else -// ii = (8*7 + 6*3)*len + 110; - ii = 8 * len + 256; - } - - ii += 2 * 10 * len; - - jj = SL811Read (hci, SL11H_SOFTMRREG); - kk = (jj & 0xFF) * 64 - ii; - - if (kk < 0) { - DBGVERBOSE - ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," - "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, - hci->active_trans); - return (-1); - } - - if (pid != PID_IN) { - /* Load data into hc */ - - SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); - } - - /* transmit */ - - SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, - toggle, slow, urb_state); - - return len; -} - -/************************************************************************ - * Function Name : hc_parse_trans - * - * This function checks the status of the transmitted or received packet - * and copy the data from the SL811HS register into a buffer. - * - * 1) Check the status of the packet - * 2) If successful, and IN packet then copy the data from the SL811HS register - * into a buffer - * - * Input: hci = data structure for the host controller - * actbytes = pointer to actual number of bytes - * data = data buffer - * cc = packet status - * length = the urb transmit length - * pid = packet ID - * urb_state = the current stage of USB transaction - * - * Return: 0 - ***********************************************************************/ -static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, - int *cc, int *toggle, int length, int pid, - int urb_state) -{ - __u8 addr; - __u8 len; - - DBGFUNC ("enter hc_parse_trans\n"); - - /* get packet status; convert ack rcvd to ack-not-rcvd */ - - *cc = (int) SL811Read (hci, SL11H_PKTSTATREG); - - if (*cc & - (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | - SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { - if (*cc & SL11H_STATMASK_OVF) - DBGERR ("parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = " - "0x%x, TX_count=0x%x\n", *cc, - SL811Read (hci, SL11H_BUFLNTHREG), - SL811Read (hci, SL11H_XFERCNTREG)); - - } else { - DBGVERBOSE ("parse trans: recv ack, cc = 0x%x, len = 0x%x, \n", - *cc, length); - - /* Successful data */ - if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { - - /* Find the base address */ - addr = SL811Read (hci, SL11H_BUFADDRREG); - - /* Find the Transmit Length */ - len = SL811Read (hci, SL11H_BUFLNTHREG); - - /* The actual data length = xmit length reg - xfer count reg */ - *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); - - if ((data != NULL) && (*actbytes > 0)) { - SL811BufRead (hci, addr, data, *actbytes); - - } else if ((data == NULL) && (*actbytes <= 0)) { - DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", - *actbytes); - return 0; - } - } else if (pid == PID_OUT) { - *actbytes = length; - } else { - // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); - } - *toggle = !*toggle; - } - - return 0; -} - -/************************************************************************ - * Function Name : hc_start_int - * - * This function enables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_start_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - int mask = - SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | - SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; - SL811Write (hci, IntEna, mask); -#endif -} - -/************************************************************************ - * Function Name : hc_stop_int - * - * This function disables SL811HS interrupts - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -static void hc_stop_int (hci_t * hci) -{ -#ifdef HC_SWITCH_INT - SL811Write (hci, SL11H_INTSTATREG, 0xff); -// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); - -#endif -} - -/************************************************************************ - * Function Name : handleInsRmvIntr - * - * This function handles the insertion or removal of device on SL811HS. - * It resets the controller and updates the port status - * - * Input: hci = data structure for the host controller - * - * Return: none - ***********************************************************************/ -void handleInsRmvIntr (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - USBReset (hci); - - /* Changes in connection status */ - - hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; - - /* Port Enable or Disable */ - - if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { - /* device is connected to the port: - * 1) Enable port - * 2) Resume ?? - */ -// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - /* Over Current is not supported by the SL811 HW ?? */ - - /* How about the Port Power ?? */ - - } else { - /* Device has disconnect: - * 1) Disable port - */ - - hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); - hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; - - } -} - -/***************************************************************** - * - * Function Name: SL11StartXaction - * - * This functions load the registers with appropriate value and - * transmit the packet. - * - * Input: hci = data structure for the host controller - * addr = USB address of the device - * epaddr = endpoint number - * pid = packet ID - * len = data length - * toggle = USB toggle bit, either 0 or 1 - * slow = speed of the device - * urb_state = the current stage of USB transaction - * - * Return: 0 = error; 1 = successful - * - *****************************************************************/ -int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, - int toggle, int slow, int urb_state) -{ - - hcipriv_t *hp = &hci->hp; - __u8 cmd = 0; - __u8 setup_data[4]; - __u16 speed; - - speed = hp->RHportStatus->portStatus; - if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { - cmd |= SL11H_HCTLMASK_PREAMBLE; - } - switch (pid) { - case PID_SETUP: - cmd &= SL11H_HCTLMASK_PREAMBLE; - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - break; - - case PID_OUT: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= - (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP | - SL11H_HCTLMASK_WRITE); - if (toggle) { - cmd |= SL11H_HCTLMASK_SEQ; - } - break; - - case PID_IN: - cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); - cmd |= (SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP); - break; - - default: - DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); - return 0; - } - setup_data[0] = SL11H_DATA_START; - setup_data[1] = len; - setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); - setup_data[3] = addr & 0x7F; - - SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); - - SL811Write (hci, SL11H_HOSTCTLREG, cmd); - -#if 0 - /* The SL811 has a hardware flaw when hub devices sends out - * SE0 between packets. It has been found in a TI chipset and - * cypress hub chipset. It causes the SL811 to hang - * The workaround is to re-issue the preample again. - */ - - if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { - SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); - SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable - } -#endif - return 1; -} - -/***************************************************************** - * - * Function Name: hc_interrupt - * - * Interrupt service routine. - * - * 1) determine the causes of interrupt - * 2) clears all interrupts - * 3) calls appropriate function to service the interrupt - * - * Input: irq = interrupt line associated with the controller - * hci = data structure for the host controller - * r = holds the snapshot of the processor's context before - * the processor entered interrupt code. (not used here) - * - * Return value : None. - * - *****************************************************************/ -static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) -{ - char ii; - hci_t *hci = __hci; - int isExcessNak = 0; - int urb_state = 0; - char tmpIrq = 0; - - /* Get value from interrupt status register */ - - ii = SL811Read (hci, SL11H_INTSTATREG); - - if (ii & SL11H_INTMASK_INSRMV) { - /* Device insertion or removal detected for the USB port */ - - SL811Write (hci, SL11H_INTENBLREG, 0); - SL811Write (hci, SL11H_CTLREG1, 0); - mdelay (100); // wait for device stable - handleInsRmvIntr (hci); - return; - } - - /* Clear all interrupts */ - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - - if (ii & SL11H_INTMASK_XFERDONE) { - /* USB Done interrupt occurred */ - - urb_state = sh_done_list (hci, &isExcessNak); -#ifdef WARNING - if (hci->td_array->len > 0) - printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", - hci->td_array->len); -#endif - if (hci->td_array->len == 0 && !isExcessNak - && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { - if (urb_state == 0) { - /* All urb_state has not been finished yet! - * continue with the current urb transaction - */ - - if (hci->last_packet_nak == 0) { - if (!usb_pipecontrol - (hci->td_array->td[0].urb->pipe)) - sh_add_packet (hci, hci->td_array-> td[0].urb); - } - } else { - /* The last transaction has completed: - * schedule the next transaction - */ - - sh_schedule_trans (hci, 0); - } - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; - } - - if (ii & SL11H_INTMASK_SOFINTR) { - hci->frame_number = (hci->frame_number + 1) % 2048; - if (hci->td_array->len == 0) - sh_schedule_trans (hci, 1); - else { - if (sofWaitCnt++ > 100) { - /* The last transaction has not completed. - * Need to retire the current td, and let - * it transmit again later on. - * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER - * GET TO THIS POINT) - */ - - DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b: 0\n", - hci->td_array->len); - urb_print (hci->td_array->td[hci->td_array->len - 1].urb, - "INTERRUPT", 0); - sh_done_list (hci, &isExcessNak); - SL811Write (hci, SL11H_INTSTATREG, 0xff); - hci->td_array->len = 0; - sofWaitCnt = 0; - } - } - tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); - if (tmpIrq) { - DBG ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - - /* If we receive a DONE IRQ after schedule, need to - * handle DONE IRQ again - */ - - if (tmpIrq & SL11H_INTMASK_XFERDONE) { - DBGERR ("IRQ occurred while service SOF: irq = 0x%x\n", - tmpIrq); - urb_state = sh_done_list (hci, &isExcessNak); - } - SL811Write (hci, SL11H_INTSTATREG, 0xff); - } - } else { - DBG ("SL811 ISR: unknown, int = 0x%x \n", ii); - } - - SL811Write (hci, SL11H_INTSTATREG, 0xff); - return; -} - -/***************************************************************** - * - * Function Name: hc_reset - * - * This function does register test and resets the SL811HS - * controller. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_reset (hci_t * hci) -{ - int attachFlag = 0; - - DBGFUNC ("Enter hc_reset\n"); - regTest (hci); - attachFlag = USBReset (hci); - if (attachFlag) { - setPortChange (hci, PORT_CONNECT_CHANGE); - } - return (0); -} - -/***************************************************************** - * - * Function Name: hc_alloc_trans_buffer - * - * This function allocates all transfer buffer - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_alloc_trans_buffer (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - int maxlen; - - hp->itl0_len = 0; - hp->itl1_len = 0; - hp->atl_len = 0; - - hp->itl_buffer_len = 1024; - hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ - - maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; - - hp->tl = kmalloc (maxlen, GFP_KERNEL); - - if (!hp->tl) - return -ENOMEM; - - memset (hp->tl, 0, maxlen); - return 0; -} - -/***************************************************************** - * - * Function Name: getPortStatusAndChange - * - * This function gets the ports status from SL811 and format it - * to a USB request format - * - * Input: hci = data structure for the host controller - * - * Return value : port status and change - * - *****************************************************************/ -static __u32 getPortStatusAndChange (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - __u32 portstatus; - - DBGFUNC ("enter getPorStatusAndChange\n"); - - portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; - - return (portstatus); -} - -/***************************************************************** - * - * Function Name: setPortChange - * - * This function set the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - - switch (bitPos) { - case PORT_CONNECT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_ENABLE_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - - case PORT_OVER_CURRENT_STAT: - hp->RHportStatus->portChange |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortChange - * - * This function clear the bit position of portChange. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortChange (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_CONNECT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_ENABLE_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_RESET_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_SUSPEND_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - - case PORT_OVER_CURRENT_CHANGE: - hp->RHportStatus->portChange &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: clrPortStatus - * - * This function clear the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void clrPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus &= ~bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: setPortStatus - * - * This function set the bit position of portStatus. - * - * Input: hci = data structure for the host controller - * bitPos = the bit position - * - * Return value : none - * - *****************************************************************/ -static void setPortStatus (hci_t * hci, __u16 bitPos) -{ - hcipriv_t *hp = &hci->hp; - switch (bitPos) { - case PORT_ENABLE_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_RESET_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_POWER_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - - case PORT_SUSPEND_STAT: - hp->RHportStatus->portStatus |= bitPos; - break; - } -} - -/***************************************************************** - * - * Function Name: hc_start - * - * This function starts the root hub functionality. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static int hc_start (hci_t * hci) -{ - DBGFUNC ("Enter hc_start\n"); - - rh_connect_rh (hci); - - return 0; -} - -/***************************************************************** - * - * Function Name: hc_alloc_hci - * - * This function allocates all data structure and store in the - * private data structure. - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static hci_t *__devinit hc_alloc_hci (void) -{ - hci_t *hci; - hcipriv_t *hp; - portstat_t *ps; - struct usb_bus *bus; - - DBGFUNC ("Enter hc_alloc_hci\n"); - hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); - if (!hci) - return NULL; - - memset (hci, 0, sizeof (hci_t)); - - hp = &hci->hp; - - hp->irq = -1; - hp->hcport = -1; - - /* setup root hub port status */ - - ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); - - if (!ps) - return NULL; - ps->portStatus = PORT_STAT_DEFAULT; - ps->portChange = PORT_CHANGE_DEFAULT; - hp->RHportStatus = ps; - - hci->nakCnt = 0; - hci->last_packet_nak = 0; - - hci->a_td_array.len = 0; - hci->i_td_array[0].len = 0; - hci->i_td_array[1].len = 0; - hci->td_array = &hci->a_td_array; - hci->active_urbs = 0; - hci->active_trans = 0; - INIT_LIST_HEAD (&hci->hci_hcd_list); - list_add (&hci->hci_hcd_list, &hci_hcd_list); - init_waitqueue_head (&hci->waitq); - - INIT_LIST_HEAD (&hci->ctrl_list); - INIT_LIST_HEAD (&hci->bulk_list); - INIT_LIST_HEAD (&hci->iso_list); - INIT_LIST_HEAD (&hci->intr_list); - INIT_LIST_HEAD (&hci->del_list); - - bus = usb_alloc_bus (&hci_device_operations); - if (!bus) { - kfree (hci); - return NULL; - } - - hci->bus = bus; - bus->bus_name = "sl811"; - bus->hcpriv = (void *) hci; - - return hci; -} - -/***************************************************************** - * - * Function Name: hc_release_hci - * - * This function De-allocate all resources - * - * Input: hci = data structure for the host controller - * - * Return value : 0 - * - *****************************************************************/ -static void hc_release_hci (hci_t * hci) -{ - hcipriv_t *hp = &hci->hp; - - DBGFUNC ("Enter hc_release_hci\n"); - - /* disconnect all devices */ - if (hci->bus->root_hub) - usb_disconnect (&hci->bus->root_hub); - - hc_reset (hci); - - if (hp->tl) - kfree (hp->tl); - - if (hp->hcport > 0) { - release_region (hp->hcport, 2); - hp->hcport = 0; - } - - if (hp->irq >= 0) { - free_irq (hp->irq, hci); - hp->irq = -1; - } - - usb_deregister_bus (hci->bus); - usb_free_bus (hci->bus); - - list_del (&hci->hci_hcd_list); - INIT_LIST_HEAD (&hci->hci_hcd_list); - - kfree (hci); -} - -/***************************************************************** - * - * Function Name: init_irq - * - * This function is board specific. It sets up the interrupt to - * be an edge trigger and trigger on the rising edge - * - * Input: none - * - * Return value : none - * - *****************************************************************/ -void init_irq (void) -{ - GPDR &= ~(1 << 13); - set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE); -} - -/***************************************************************** - * - * Function Name: hc_found_hci - * - * This function request IO memory regions, request IRQ, and - * allocate all other resources. - * - * Input: addr = first IO address - * addr2 = second IO address - * irq = interrupt number - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __devinit hc_found_hci (int addr, int addr2, int irq) -{ - hci_t *hci; - hcipriv_t *hp; - - DBGFUNC ("Enter hc_found_hci\n"); - hci = hc_alloc_hci (); - if (!hci) { - return -ENOMEM; - } - - init_irq (); - hp = &hci->hp; - - if (!request_region (addr, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport = addr; - if (!hp->hcport) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport); - } - - if (!request_region (addr2, 256, "SL811 USB HOST")) { - DBGERR ("request address %d failed", addr2); - hc_release_hci (hci); - return -EBUSY; - } - hp->hcport2 = addr2; - if (!hp->hcport2) { - DBGERR ("Error mapping SL811 Memory 0x%x", hp->hcport2); - } - - if (hc_alloc_trans_buffer (hci)) { - hc_release_hci (hci); - return -ENOMEM; - } - - usb_register_bus (hci->bus); - - if (request_irq (irq, hc_interrupt, 0, "SL811", hci) != 0) { - DBGERR ("request interrupt %d failed", irq); - hc_release_hci (hci); - return -EBUSY; - } - hp->irq = irq; - - printk (KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", - addr, addr2, irq); - hc_reset (hci); - - if (hc_start (hci) < 0) { - DBGERR ("can't start usb-%x", addr); - hc_release_hci (hci); - return -EBUSY; - } - - return 0; -} - -/***************************************************************** - * - * Function Name: hci_hcd_init - * - * This is an init function, and it is the first function being called - * - * Input: none - * - * Return: 0 = success or error condition - * - *****************************************************************/ -static int __init hci_hcd_init (void) -{ - int ret; - - DBGFUNC ("Enter hci_hcd_init\n"); - ret = hc_found_hci (base_addr, data_reg_addr, irq); - - return ret; -} - -/***************************************************************** - * - * Function Name: hci_hcd_cleanup - * - * This is a cleanup function, and it is called when module is - * unloaded. - * - * Input: none - * - * Return: none - * - *****************************************************************/ -static void __exit hci_hcd_cleanup (void) -{ - struct list_head *hci_l; - hci_t *hci; - - DBGFUNC ("Enter hci_hcd_cleanup\n"); - for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { - hci = list_entry (hci_l, hci_t, hci_hcd_list); - hci_l = hci_l->next; - hc_release_hci (hci); - } -} - -module_init (hci_hcd_init); -module_exit (hci_hcd_cleanup); - -MODULE_AUTHOR ("Pei Liu "); -MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff -Nru a/drivers/usb/hc_sl811.h b/drivers/usb/hc_sl811.h --- a/drivers/usb/hc_sl811.h Fri Oct 24 17:01:14 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,385 +0,0 @@ -/* - * SL811HS HCD (Host Controller Driver) for USB. - * - * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC - * - * - */ - -#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) - -/* - * Maximum number of root hub ports - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* control and status registers */ -#define HcRevision 0x00 -#define HcControl 0x01 -#define HcCommandStatus 0x02 -#define HcInterruptStatus 0x03 -#define HcInterruptEnable 0x04 -#define HcInterruptDisable 0x05 -#define HcFmInterval 0x0D -#define HcFmRemaining 0x0E -#define HcFmNumber 0x0F -#define HcLSThreshold 0x11 -#define HcRhDescriptorA 0x12 -#define HcRhDescriptorB 0x13 -#define HcRhStatus 0x14 -#define HcRhPortStatus 0x15 - -#define HcHardwareConfiguration 0x20 -#define HcDMAConfiguration 0x21 -#define HcTransferCounter 0x22 -#define HcuPInterrupt 0x24 -#define HcuPInterruptEnable 0x25 -#define HcChipID 0x27 -#define HcScratch 0x28 -#define HcSoftwareReset 0x29 -#define HcITLBufferLength 0x2A -#define HcATLBufferLength 0x2B -#define HcBufferStatus 0x2C -#define HcReadBackITL0Length 0x2D -#define HcReadBackITL1Length 0x2E -#define HcITLBufferPort 0x40 -#define HcATLBufferPort 0x41 - -/* OHCI CONTROL AND STATUS REGISTER MASKS */ - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -#define OHCI_USB_RESET (0 << 6) -#define OHCI_USB_RESUME (1 << 6) -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_SO (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ - -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ - -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - -/* - * HcHardwareConfiguration - */ -#define InterruptPinEnable (1 << 0) -#define InterruptPinTrigger (1 << 1) -#define InterruptOutputPolarity (1 << 2) -#define DataBusWidth16 (1 << 3) -#define DREQOutputPolarity (1 << 5) -#define DACKInputPolarity (1 << 6) -#define EOTInputPolarity (1 << 7) -#define DACKMode (1 << 8) -#define AnalogOCEnable (1 << 10) -#define SuspendClkNotStop (1 << 11) -#define DownstreamPort15KRSel (1 << 12) - -/* - * HcDMAConfiguration - */ -#define DMAReadWriteSelect (1 << 0) -#define ITL_ATL_DataSelect (1 << 1) -#define DMACounterSelect (1 << 2) -#define DMAEnable (1 << 4) -#define BurstLen_1 0 -#define BurstLen_4 (1 << 5) -#define BurstLen_8 (2 << 5) - -/* - * HcuPInterrupt - */ -#define SOFITLInt (1 << 0) -#define ATLInt (1 << 1) -#define AllEOTInterrupt (1 << 2) -#define OPR_Reg (1 << 4) -#define HCSuspended (1 << 5) -#define ClkReady (1 << 6) - -/* - * HcBufferStatus - */ -#define ITL0BufferFull (1 << 0) -#define ITL1BufferFull (1 << 1) -#define ATLBufferFull (1 << 2) -#define ITL0BufferDone (1 << 3) -#define ITL1BufferDone (1 << 4) -#define ATLBufferDone (1 << 5) - -/* OHCI ROOT HUB REGISTER MASKS */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status */ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#define URB_DEL 1 - -#define PORT_STAT_DEFAULT 0x0100 -#define PORT_CONNECT_STAT 0x1 -#define PORT_ENABLE_STAT 0x2 -#define PORT_SUSPEND_STAT 0x4 -#define PORT_OVER_CURRENT_STAT 0x8 -#define PORT_RESET_STAT 0x10 -#define PORT_POWER_STAT 0x100 -#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 - -#define PORT_CHANGE_DEFAULT 0x0 -#define PORT_CONNECT_CHANGE 0x1 -#define PORT_ENABLE_CHANGE 0x2 -#define PORT_SUSPEND_CHANGE 0x4 -#define PORT_OVER_CURRENT_CHANGE 0x8 -#define PORT_RESET_CHANGE 0x10 - -/* Port Status Request info */ - -typedef struct portstat { - __u16 portChange; - __u16 portStatus; -} portstat_t; - -typedef struct hcipriv { - int irq; - int disabled; /* e.g. got a UE, we're hung */ - atomic_t resume_count; /* defending against multiple resumes */ - struct ohci_regs *regs; /* OHCI controller's memory */ - int hcport; /* I/O base address */ - int hcport2; /* I/O data reg addr */ - - struct portstat *RHportStatus; /* root hub port status */ - - int intrstatus; - __u32 hc_control; /* copy of the hc control reg */ - - int frame; - - __u8 *tl; - int xferPktLen; - int atl_len; - int atl_buffer_len; - int itl0_len; - int itl1_len; - int itl_buffer_len; - int itl_index; - int tl_last; - int units_left; - -} hcipriv_t; -struct hci; - -#define cClt 0 // Control -#define cISO 1 // ISO -#define cBULK 2 // BULK -#define cInt 3 // Interrupt -#define ISO_BIT 0x10 - -/*------------------------------------------------------------------------- - * EP0 use for configuration and Vendor Specific command interface - *------------------------------------------------------------------------*/ -#define cMemStart 0x10 -#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ -#define EP0Len 0x40 /* Length of config buffer EP0Buf */ -#define EP1Buf 0x60 -#define EP1Len 0x40 - -/*------------------------------------------------------------------------- - * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. - *------------------------------------------------------------------------*/ -#define uBufA 0x80 /* buffer A address for DATA0 */ -#define uBufB 0xc0 /* buffer B address for DATA1 */ -#define uXferLen 0x40 /* xfer length */ -#define sMemSize 0xc0 /* Total SL11 memory size */ -#define cMemEnd 256 - -/*------------------------------------------------------------------------- - * SL811H Register Control memory map - * --Note: - * --SL11H only has one control register set from 0x00-0x04 - * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c - *------------------------------------------------------------------------*/ - -#define EP0Control 0x00 -#define EP0Address 0x01 -#define EP0XferLen 0x02 -#define EP0Status 0x03 -#define EP0Counter 0x04 - -#define EP1Control 0x08 -#define EP1Address 0x09 -#define EP1XferLen 0x0a -#define EP1Status 0x0b -#define EP1Counter 0x0c - -#define CtrlReg 0x05 -#define IntEna 0x06 - // 0x07 is reserved -#define IntStatus 0x0d -#define cDATASet 0x0e -#define cSOFcnt 0x0f -#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ -#define HostMask 0x47 /* Host request command for IntStatus */ -#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ - -/*------------------------------------------------------------------------- - * Standard Chapter 9 definition - *------------------------------------------------------------------------- - */ -#define GET_STATUS 0x00 -#define CLEAR_FEATURE 0x01 -#define SET_FEATURE 0x03 -#define SET_ADDRESS 0x05 -#define GET_DESCRIPTOR 0x06 -#define SET_DESCRIPTOR 0x07 -#define GET_CONFIG 0x08 -#define SET_CONFIG 0x09 -#define GET_INTERFACE 0x0a -#define SET_INTERFACE 0x0b -#define SYNCH_FRAME 0x0c - -#define DEVICE 0x01 -#define CONFIGURATION 0x02 -#define STRING 0x03 -#define INTERFACE 0x04 -#define ENDPOINT 0x05 - -/*------------------------------------------------------------------------- - * SL11H/SL811H definition - *------------------------------------------------------------------------- - */ -#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) -#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) -#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) -#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) -#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) -#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) - -#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition -#define PID_SOF 0xA5 -#define PID_IN 0x69 -#define PID_OUT 0xe1 - -#define MAX_RETRY 0xffff -#define TIMEOUT 5 /* 2 mseconds */ - -#define SL11H_HOSTCTLREG 0 -#define SL11H_BUFADDRREG 1 -#define SL11H_BUFLNTHREG 2 -#define SL11H_PKTSTATREG 3 /* read */ -#define SL11H_PIDEPREG 3 /* write */ -#define SL11H_XFERCNTREG 4 /* read */ -#define SL11H_DEVADDRREG 4 /* write */ -#define SL11H_CTLREG1 5 -#define SL11H_INTENBLREG 6 - -#define SL11H_HOSTCTLREG_B 8 -#define SL11H_BUFADDRREG_B 9 -#define SL11H_BUFLNTHREG_B 0x0A -#define SL11H_PKTSTATREG_B 0x0B /* read */ -#define SL11H_PIDEPREG_B 0x0B /* write */ -#define SL11H_XFERCNTREG_B 0x0C /* read */ -#define SL11H_DEVADDRREG_B 0x0C /* write */ - -#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ -#define SL11H_HWREVREG 0x0E /* read */ -#define SL11H_SOFLOWREG 0x0E /* write */ -#define SL11H_SOFTMRREG 0x0F /* read */ -#define SL11H_CTLREG2 0x0F /* write */ -#define SL11H_DATA_START 0x10 - -/* Host control register bits (addr 0) */ -#define SL11H_HCTLMASK_ARM 1 -#define SL11H_HCTLMASK_ENBLEP 2 -#define SL11H_HCTLMASK_WRITE 4 -#define SL11H_HCTLMASK_ISOCH 0x10 -#define SL11H_HCTLMASK_AFTERSOF 0x20 -#define SL11H_HCTLMASK_SEQ 0x40 -#define SL11H_HCTLMASK_PREAMBLE 0x80 - -/* Packet status register bits (addr 3) */ -#define SL11H_STATMASK_ACK 1 -#define SL11H_STATMASK_ERROR 2 -#define SL11H_STATMASK_TMOUT 4 -#define SL11H_STATMASK_SEQ 8 -#define SL11H_STATMASK_SETUP 0x10 -#define SL11H_STATMASK_OVF 0x20 -#define SL11H_STATMASK_NAK 0x40 -#define SL11H_STATMASK_STALL 0x80 - -/* Control register 1 bits (addr 5) */ -#define SL11H_CTL1MASK_DSBLSOF 1 -#define SL11H_CTL1MASK_NOTXEOF2 4 -#define SL11H_CTL1MASK_DSTATE 0x18 -#define SL11H_CTL1MASK_NSPD 0x20 -#define SL11H_CTL1MASK_SUSPEND 0x40 -#define SL11H_CTL1MASK_CLK12 0x80 - -#define SL11H_CTL1VAL_RESET 8 - -/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ -#define SL11H_INTMASK_XFERDONE 1 -#define SL11H_INTMASK_SOFINTR 0x10 -#define SL11H_INTMASK_INSRMV 0x20 -#define SL11H_INTMASK_USBRESET 0x40 -#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ - -/* HW rev and SOF lo register bits (addr 0xE) */ -#define SL11H_HWRMASK_HWREV 0xF0 - -/* SOF counter and control reg 2 (addr 0xF) */ -#define SL11H_CTL2MASK_SOFHI 0x3F -#define SL11H_CTL2MASK_DSWAP 0x40 -#define SL11H_CTL2MASK_HOSTMODE 0xae - diff -Nru a/drivers/usb/hc_sl811_rh.c b/drivers/usb/hc_sl811_rh.c --- a/drivers/usb/hc_sl811_rh.c Fri Oct 24 17:01:14 2003 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,526 +0,0 @@ - -/*-------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------* - * SL811HS virtual root hub - * - * based on usb-ohci.c by R. Weissgaerber et al. - *-------------------------------------------------------------------------* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - *-------------------------------------------------------------------------*/ - -#ifdef DEBUG -#undef DEBUG -#endif -static __u32 getPortStatusAndChange (hci_t * hci); -static void setPortStatus (hci_t * hci, __u16 bitPos); -static void setPortChange (hci_t * hci, __u16 bitPos); -static void clrPortStatus (hci_t * hci, __u16 bitPos); -static void clrPortChange (hci_t * hci, __u16 bitPos); -static int USBReset (hci_t * hci); -static int cc_to_error (int cc); - -/*-------------------------------------------------------------------------* - * Virtual Root Hub - *-------------------------------------------------------------------------*/ - -/* Device descriptor */ -static __u8 root_hub_dev_des[] = { - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x10, /* __u16 bcdUSB; v1.1 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = { - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, - 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* Hub class-specific descriptor is constructed dynamically */ - -/*************************************************************************** - * Function Name : rh_send_irq - * - * This function examine the port change in the virtual root hub. - * - * Note: This function assumes only one port exist in the root hub. - * - * Input: hci = data structure for the host controller - * rh_data = The pointer to port change data - * rh_len = length of the data in bytes - * - * Return: length of data - **************************************************************************/ -static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) -{ - int num_ports; - int i; - int ret; - int len; - __u8 data[8]; - - DBGFUNC ("enter rh_send_irq: \n"); - - /* Assuming the root hub has one port. This value need to change if - * there are more than one port for the root hub - */ - - num_ports = 1; - - /* The root hub status is not implemented, it basically has two fields: - * -- Local Power Status - * -- Over Current Indicator - * -- Local Power Change - * -- Over Current Indicator - * - * Right now, It is assume the power is good and no changes - */ - - *(__u8 *) data = 0; - - ret = *(__u8 *) data; - - /* Has the port status change within the root hub: It checks for - * -- Port Connect Status change - * -- Port Enable Change - */ - - for (i = 0; i < num_ports; i++) { - *(__u8 *) (data + (i + 1) / 8) |= - (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); - ret += *(__u8 *) (data + (i + 1) / 8); - - /* After the port change is read, it should be reset so the next time - * is it doesn't trigger a change again */ - - } - len = i / 8 + 1; - - if (ret > 0) { - memcpy (rh_data, data, min (len, min (rh_len, (int)sizeof (data)))); - return len; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_int_timer_do - * - * This function is called when the timer expires. It gets the the port - * change data and pass along to the upper protocol. - * - * Note: The virtual root hub interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: ptr = ptr to the urb - * - * Return: none - **************************************************************************/ -static void rh_int_timer_do (unsigned long ptr) -{ - int len; - struct urb *urb = (struct urb *) ptr; - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_int_timer_do\n"); - - if (hci->rh.send) { - len = rh_send_irq (hci, urb->transfer_buffer, - urb->transfer_buffer_length); - if (len > 0) { - urb->actual_length = len; - if (urb_debug == 2) - urb_print (urb, "RET-t(rh)", - usb_pipeout (urb->pipe)); - - if (urb->complete) { - urb->complete (urb); - } - } - } - - /* re-activate the timer */ - rh_init_int_timer (urb); -} - -/*************************************************************************** - * Function Name : rh_init_int_timer - * - * This function creates a timer that act as interrupt pipe in the - * virtual hub. - * - * Note: The virtual root hub's interrupt pipe are polled by the timer - * every "interval" ms - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_init_int_timer (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - hci->rh.interval = urb->interval; - - init_timer (&hci->rh.rh_int_timer); - hci->rh.rh_int_timer.function = rh_int_timer_do; - hci->rh.rh_int_timer.data = (unsigned long) urb; - hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; - add_timer (&hci->rh.rh_int_timer); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* helper macro */ -#define OK(x) len = (x); break - -/*************************************************************************** - * Function Name : rh_submit_urb - * - * This function handles all USB request to the the virtual root hub - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_submit_urb (struct urb * urb) -{ - struct usb_device *usb_dev = urb->dev; - hci_t *hci = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = TD_CC_NOERROR; - __u32 datab[4]; - __u8 *data_buf = (__u8 *) datab; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBGFUNC ("enter rh_submit_urb\n"); - if (usb_pipeint (pipe)) { - hci->rh.urb = urb; - hci->rh.send = 1; - hci->rh.interval = urb->interval; - rh_init_int_timer (urb); - urb->status = cc_to_error (TD_CC_NOERROR); - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu (cmd->wValue); - wIndex = le16_to_cpu (cmd->wIndex); - wLength = le16_to_cpu (cmd->wLength); - - DBG ("rh_submit_urb, req = %d(%x) len=%d", - bmRType_bReq, bmRType_bReq, wLength); - - switch (bmRType_bReq) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32 (0); - OK (4); - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = - cpu_to_le32 (getPortStatusAndChange (hci)); - OK (4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case RH_C_HUB_LOCAL_POWER: - OK (0); - - case (RH_C_HUB_OVER_CURRENT): - /* Over Current Not Implemented */ - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - clrPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_PORT_SUSPEND): - clrPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_POWER): - clrPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_C_PORT_CONNECTION): - clrPortChange (hci, PORT_CONNECT_STAT); - OK (0); - - case (RH_C_PORT_ENABLE): - clrPortChange (hci, PORT_ENABLE_STAT); - OK (0); - - case (RH_C_PORT_SUSPEND): - clrPortChange (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_C_PORT_OVER_CURRENT): - clrPortChange (hci, PORT_OVER_CURRENT_STAT); - OK (0); - - case (RH_C_PORT_RESET): - clrPortChange (hci, PORT_RESET_STAT); - OK (0); - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - setPortStatus (hci, PORT_SUSPEND_STAT); - OK (0); - - case (RH_PORT_RESET): - setPortStatus (hci, PORT_RESET_STAT); - // USBReset(hci); - clrPortChange (hci, - PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE - | PORT_SUSPEND_CHANGE | - PORT_OVER_CURRENT_CHANGE); - setPortChange (hci, PORT_RESET_CHANGE); - clrPortStatus (hci, PORT_RESET_STAT); - setPortStatus (hci, PORT_ENABLE_STAT); - - OK (0); - - case (RH_PORT_POWER): - setPortStatus (hci, PORT_POWER_STAT); - OK (0); - - case (RH_PORT_ENABLE): - setPortStatus (hci, PORT_ENABLE_STAT); - OK (0); - } - break; - - case RH_SET_ADDRESS: - hci->rh.devnum = wValue; - OK (0); - - case RH_GET_DESCRIPTOR: - DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); - data_buf = root_hub_dev_des; - OK (len); - - case (0x02): /* configuration descriptor */ - len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); - data_buf = root_hub_config_des; - OK (len); - - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, (int) (long) 0, - "SL811HS", data, wLength); - if (len > 0) { - data_buf = data; - OK (min (leni, len)); - } - - default: - status = SL11H_STATMASK_STALL; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - data_buf[0] = 9; // min length; - data_buf[1] = 0x29; - data_buf[2] = 1; // # of downstream port - data_buf[3] = 0; - datab[1] = 0; - data_buf[5] = 50; // 100 ms for port reset - data_buf[7] = 0xfc; // which port is attachable - if (data_buf[2] < 7) { - data_buf[8] = 0xff; - } else { - } - - len = min (leni, min ((__u16)data_buf[0], wLength)); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data_buf = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - DBGERR ("unsupported root hub command"); - status = SL11H_STATMASK_STALL; - } - - len = min (len, leni); - if (data != data_buf) - memcpy (data, data_buf, len); - urb->actual_length = len; - urb->status = cc_to_error (status); - - urb->hcpriv = NULL; - urb->dev = NULL; - if (urb->complete) { - urb->complete (urb); - } - - return 0; -} - -/*************************************************************************** - * Function Name : rh_unlink_urb - * - * This function unlinks the URB - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_unlink_urb (struct urb * urb) -{ - hci_t *hci = urb->dev->bus->hcpriv; - - DBGFUNC ("enter rh_unlink_urb\n"); - if (hci->rh.urb == urb) { - hci->rh.send = 0; - del_timer (&hci->rh.rh_int_timer); - hci->rh.urb = NULL; - - urb->hcpriv = NULL; - usb_put_dev (urb->dev); - urb->dev = NULL; - if (urb->transfer_flags & USB_ASYNC_UNLINK) { - urb->status = -ECONNRESET; - if (urb->complete) { - urb->complete (urb); - } - } else - urb->status = -ENOENT; - } - return 0; -} - -/*************************************************************************** - * Function Name : rh_connect_rh - * - * This function connect the virtual root hub to the USB stack - * - * Input: urb = USB request block - * - * Return: 0 - **************************************************************************/ -static int rh_connect_rh (hci_t * hci) -{ - struct usb_device *usb_dev; - - hci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, hci->bus); - if (!usb_dev) - return -ENOMEM; - - hci->bus->root_hub = usb_dev; - usb_connect (usb_dev); - if (usb_new_device (usb_dev) != 0) { - usb_free_dev (usb_dev); - return -ENODEV; - } - - return 0; -} diff -Nru a/drivers/usb/host/Config.in b/drivers/usb/host/Config.in --- a/drivers/usb/host/Config.in Fri Oct 24 17:01:14 2003 +++ b/drivers/usb/host/Config.in Fri Oct 24 17:01:14 2003 @@ -12,6 +12,8 @@ define_bool CONFIG_USB_UHCI_ALT n fi dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB -if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' SL811HS Alternate (support isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB +if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" ]; then + # Cypress embedded USB controller on StrongARM or on x86 in PC/104 + dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL fi diff -Nru a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile --- a/drivers/usb/host/Makefile Fri Oct 24 17:01:14 2003 +++ b/drivers/usb/host/Makefile Fri Oct 24 17:01:14 2003 @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_UHCI) += usb-uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o +obj-$(CONFIG_USB_SL811HS) += hc_sl811.o # Extract lists of the multi-part drivers. # The 'int-*' lists are the intermediate files used to build the multi's. diff -Nru a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/hc_simple.c Fri Oct 24 17:01:14 2003 @@ -0,0 +1,1089 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * simple generic USB HCD frontend Version 0.9.5 (10/28/2001) + * for embedded HCs (SL811HS) + * + * USB URB handling, hci_ hcs_ + * URB queueing, qu_ + * Transfer scheduling, sh_ + * + * + *-------------------------------------------------------------------------* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +/* main lock for urb access */ +static spinlock_t usb_urb_lock = SPIN_LOCK_UNLOCKED; + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/* URB HCD API function layer + * * * */ + +/*************************************************************************** + * Function Name : hcs_urb_queue + * + * This function initializes the urb status and length before queueing the + * urb. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 + **************************************************************************/ +static inline int hcs_urb_queue (hci_t * hci, struct urb * urb) +{ + int i; + + DBGFUNC ("enter hcs_urb_queue\n"); + if (usb_pipeisoc (urb->pipe)) { + DBGVERBOSE ("hcs_urb_queue: isoc pipe\n"); + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + + /* urb->next hack : 1 .. resub, 0 .. single shot */ + /* urb->interval = urb->next ? 1 : 0; */ + } + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; + + if (usb_pipecontrol (urb->pipe)) + hc_flush_data_cache (hci, urb->setup_packet, 8); + if (usb_pipeout (urb->pipe)) + hc_flush_data_cache (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + + qu_queue_urb (hci, urb); + + return 0; +} + +/*************************************************************************** + * Function Name : hcs_return_urb + * + * This function the return path of URB back to the USB core. It calls the + * the urb complete function if exist, and also handles the resubmition of + * interrupt URBs. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit + * + * Return: 0 + **************************************************************************/ +static int hcs_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct usb_device *dev = urb->dev; + int resubmit = 0; + + DBGFUNC ("enter hcs_return_urb, urb pointer = %p, " + "transferbuffer point = %p, " + " setup packet pointer = %p, context pointer = %p \n", + (__u32 *) urb, (__u32 *) urb->transfer_buffer, + (__u32 *) urb->setup_packet, (__u32 *) urb->context); + if (urb_debug) + urb_print (urb, "RET", usb_pipeout (urb->pipe)); + + resubmit = urb->interval && resub_ok; + + urb->dev = urb->hcpriv = NULL; + + if (urb->complete) { + urb->complete (urb); /* call complete */ + } + + if (resubmit) { + /* requeue the URB */ + urb->dev = dev; + hcs_urb_queue (hci, urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_submit_urb + * + * This function is called by the USB core API when an URB is available to + * process. This function does the following + * + * 1) Check the validity of the URB + * 2) Parse the device number from the URB + * 3) Pass the URB to the root hub routine if its intended for the hub, else + * queue the urb for the attached device. + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_submit_urb (struct urb * urb) +{ + hci_t *hci; + unsigned int pipe = urb->pipe; + unsigned long flags; + int ret; + + DBGFUNC ("enter hci_submit_urb, pipe = 0x%x\n", urb->pipe); + if (!urb->dev || !urb->dev->bus || urb->hcpriv) + return -EINVAL; + + if (usb_endpoint_halted + (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) { + printk ("hci_submit_urb: endpoint_halted\n"); + return -EPIPE; + } + hci = (hci_t *) urb->dev->bus->hcpriv; + + /* a request to the virtual root hub */ + + if (usb_pipedevice (pipe) == hci->rh.devnum) { + if (urb_debug > 1) + urb_print (urb, "SUB-RH", usb_pipein (pipe)); + + return rh_submit_urb (urb); + } + + /* queue the URB to its endpoint-queue */ + + spin_lock_irqsave (&usb_urb_lock, flags); + ret = hcs_urb_queue (hci, urb); + if (ret != 0) { + /* error on return */ + DBGERR + ("hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x\n", + ret, urb->status); + } + + spin_unlock_irqrestore (&usb_urb_lock, flags); + + return ret; + +} + +/*************************************************************************** + * Function Name : hci_unlink_urb + * + * This function mark the URB to unlink + * + * Input: urb = USB request block data structure + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_unlink_urb (struct urb * urb) +{ + unsigned long flags; + hci_t *hci; + DECLARE_WAITQUEUE (wait, current); + void *comp = NULL; + + DBGFUNC ("enter hci_unlink_urb\n"); + + if (!urb) /* just to be sure */ + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + hci = (hci_t *) urb->dev->bus->hcpriv; + + /* a request to the virtual root hub */ + if (usb_pipedevice (urb->pipe) == hci->rh.devnum) { + return rh_unlink_urb (urb); + } + + if (urb_debug) + urb_print (urb, "UNLINK", 1); + + spin_lock_irqsave (&usb_urb_lock, flags); + + if (!list_empty (&urb->urb_list) && urb->status == -EINPROGRESS) { + /* URB active? */ + + if (urb-> + transfer_flags & (USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED)) { + /* asynchron with callback */ + + list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&urb->urb_list, &hci->del_list); + spin_unlock_irqrestore (&usb_urb_lock, flags); + + } else { + /* synchron without callback */ + + add_wait_queue (&hci->waitq, &wait); + + set_current_state (TASK_UNINTERRUPTIBLE); + comp = urb->complete; + urb->complete = NULL; + +/* --> crash --> */ list_del (&urb->urb_list); /* relink the urb to the del list */ + list_add (&urb->urb_list, &hci->del_list); + + spin_unlock_irqrestore (&usb_urb_lock, flags); + + schedule_timeout (HZ / 50); + + if (!list_empty (&urb->urb_list)) + list_del (&urb->urb_list); + + urb->complete = comp; + urb->hcpriv = NULL; + remove_wait_queue (&hci->waitq, &wait); + } + } else { + /* hcd does not own URB but we keep the driver happy anyway */ + spin_unlock_irqrestore (&usb_urb_lock, flags); + + if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { + urb->status = -ENOENT; + urb->actual_length = 0; + urb->complete (urb); + urb->status = 0; + } else { + urb->status = -ENOENT; + } + } + + return 0; +} + +/*************************************************************************** + * Function Name : hci_alloc_dev + * + * This function allocates private data space for the usb device and + * initialize the endpoint descriptor heads. + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 if success or error code + **************************************************************************/ +static int hci_alloc_dev (struct usb_device *usb_dev) +{ + struct hci_device *dev; + int i; + + DBGFUNC ("enter hci_alloc_dev\n"); + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset (dev, 0, sizeof (*dev)); + + for (i = 0; i < 32; i++) { + INIT_LIST_HEAD (&(dev->ed[i].urb_queue)); + dev->ed[i].pipe_head = NULL; + } + + usb_dev->hcpriv = dev; + + DBGVERBOSE ("USB HC dev alloc %d bytes\n", sizeof (*dev)); + + return 0; + +} + +/*************************************************************************** + * Function Name : hci_free_dev + * + * This function de-allocates private data space for the usb devic + * + * Input: usb_dev = pointer to the usb device + * + * Return: 0 + **************************************************************************/ +static int hci_free_dev (struct usb_device *usb_dev) +{ + DBGFUNC ("enter hci_free_dev\n"); + + if (usb_dev->hcpriv) + kfree (usb_dev->hcpriv); + + usb_dev->hcpriv = NULL; + + return 0; +} + +/*************************************************************************** + * Function Name : hci_get_current_frame_number + * + * This function get the current USB frame number + * + * Input: usb_dev = pointer to the usb device + * + * Return: frame number + **************************************************************************/ +static int hci_get_current_frame_number (struct usb_device *usb_dev) +{ + hci_t *hci = usb_dev->bus->hcpriv; + DBGFUNC ("enter hci_get_current_frame_number, frame = 0x%x \r\n", + hci->frame_number); + + return (hci->frame_number); +} + +/*************************************************************************** + * List of all io-functions + **************************************************************************/ + +static struct usb_operations hci_device_operations = { + allocate: hci_alloc_dev, + deallocate: hci_free_dev, + get_frame_number: hci_get_current_frame_number, + submit_urb: hci_submit_urb, + unlink_urb: hci_unlink_urb, +}; + +/*************************************************************************** + * URB queueing: + * + * For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of + * active URBs. + * (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list) + * For every endpoint the head URB of the queued URBs is linked to one of + * those lists. + * + * The rest of the queued URBs of an endpoint are linked into a + * private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue) + * hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is + * in one of the active URB lists. + * + * The index of an endpoint consists of its number and its direction. + * + * The state of an intr and iso URB is 0. + * For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK + * Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet) + * + **************************************************************************/ + +/*************************************************************************** + * Function Name : qu_urb_timeout + * + * This function is called when the URB timeout. The function unlinks the + * URB. + * + * Input: lurb: URB + * + * Return: none + **************************************************************************/ +#ifdef HC_URB_TIMEOUT +static void qu_urb_timeout (unsigned long lurb) +{ + struct urb *urb = (struct urb *) lurb; + + DBGFUNC ("enter qu_urb_timeout\n"); + urb->transfer_flags |= USB_TIMEOUT_KILLED; + hci_unlink_urb (urb); +} +#endif + +/*************************************************************************** + * Function Name : qu_pipeindex + * + * This function gets the index of the pipe. + * + * Input: pipe: the urb pipe + * + * Return: index + **************************************************************************/ +static inline int qu_pipeindex (__u32 pipe) +{ + DBGFUNC ("enter qu_pipeindex\n"); + return (usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe) ? 0 : usb_pipeout (pipe)); +} + +/*************************************************************************** + * Function Name : qu_seturbstate + * + * This function set the state of the URB. + * + * control pipe: 3 states -- Setup, data, status + * interrupt and bulk pipe: 1 state -- data + * + * Input: urb = USB request block data structure + * state = the urb state + * + * Return: none + **************************************************************************/ +static inline void qu_seturbstate (struct urb * urb, int state) +{ + DBGFUNC ("enter qu_seturbstate\n"); + urb->pipe &= ~0x1f; + urb->pipe |= state & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_urbstate + * + * This function get the current state of the URB. + * + * Input: urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static inline int qu_urbstate (struct urb * urb) +{ + + DBGFUNC ("enter qu_urbstate\n"); + + return urb->pipe & 0x1f; +} + +/*************************************************************************** + * Function Name : qu_queue_active_urb + * + * This function adds the urb to the appropriate active urb list and set + * the urb state. + * + * There are four active lists: isochoronous list, interrupt list, + * control list, and bulk list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * ed = endpoint descriptor + * + * Return: none + **************************************************************************/ +static inline void qu_queue_active_urb (hci_t * hci, struct urb * urb, epd_t * ed) +{ + int urb_state = 0; + DBGFUNC ("enter qu_queue_active_urb\n"); + switch (usb_pipetype (urb->pipe)) { + case PIPE_CONTROL: + list_add (&urb->urb_list, &hci->ctrl_list); + urb_state = US_CTRL_SETUP; + break; + + case PIPE_BULK: + list_add (&urb->urb_list, &hci->bulk_list); + if ((urb->transfer_flags & USB_ZERO_PACKET) + && urb->transfer_buffer_length > 0 + && + ((urb->transfer_buffer_length % + usb_maxpacket (urb->dev, urb->pipe, + usb_pipeout (urb->pipe))) == 0)) { + urb_state = US_BULK0; + } + break; + + case PIPE_INTERRUPT: + urb->start_frame = hci->frame_number; + list_add (&urb->urb_list, &hci->intr_list); + break; + + case PIPE_ISOCHRONOUS: + list_add (&urb->urb_list, &hci->iso_list); + break; + } + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) { + ed->timeout.data = (unsigned long) urb; + ed->timeout.expires = urb->timeout + jiffies; + ed->timeout.function = qu_urb_timeout; + add_timer (&ed->timeout); + } +#endif + + qu_seturbstate (urb, urb_state); +} + +/*************************************************************************** + * Function Name : qu_queue_urb + * + * This function adds the urb to the endpoint descriptor list + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: none + **************************************************************************/ +static int qu_queue_urb (hci_t * hci, struct urb * urb) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("Enter qu_queue_urb\n"); + + /* for ISOC transfers calculate start frame index */ + + if (usb_pipeisoc (urb->pipe) && urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ((ed->pipe_head) ? (ed->last_iso + 1) : hci_get_current_frame_number (urb-> dev) + 1) & 0xffff; + } + + if (ed->pipe_head) { + __list_add (&urb->urb_list, ed->urb_queue.prev, + &(ed->urb_queue)); + } else { + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + if (++hci->active_urbs == 1) + hc_start_int (hci); + } + + return 0; +} + +/*************************************************************************** + * Function Name : qu_next_urb + * + * This function removes the URB from the queue and add the next URB to + * active list. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_next_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct hci_device *hci_dev = usb_to_hci (urb->dev); + epd_t *ed = &hci_dev->ed[qu_pipeindex (urb->pipe)]; + + DBGFUNC ("enter qu_next_urb\n"); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + if (ed->pipe_head == urb) { + +#ifdef HC_URB_TIMEOUT + if (urb->timeout) + del_timer (&ed->timeout); +#endif + + if (!--hci->active_urbs) + hc_stop_int (hci); + + if (!list_empty (&ed->urb_queue)) { + urb = list_entry (ed->urb_queue.next, struct urb, urb_list); + list_del (&urb->urb_list); + INIT_LIST_HEAD (&urb->urb_list); + ed->pipe_head = urb; + qu_queue_active_urb (hci, urb, ed); + } else { + ed->pipe_head = NULL; + urb = NULL; + } + } + return urb; +} + +/*************************************************************************** + * Function Name : qu_return_urb + * + * This function is part of the return path. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * resub_ok = resubmit flag + * + * Return: pointer to the next urb + **************************************************************************/ +static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) +{ + struct urb *next_urb; + + DBGFUNC ("enter qu_return_rub\n"); + next_urb = qu_next_urb (hci, urb, resub_ok); + hcs_return_urb (hci, urb, resub_ok); + return next_urb; +} + +#if 0 /* unused now (hne) */ +/*************************************************************************** + * Function Name : sh_scan_iso_urb_list + * + * This function goes throught the isochronous urb list and schedule the + * the transfer. + * + * Note: This function has not tested yet + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * frame_number = the frame number + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_iso_urb_list (hci_t * hci, struct list_head *list_lh, + int frame_number) +{ + struct list_head *lh = list_lh->next; + struct urb *urb; + + DBGFUNC ("enter sh_scan_iso_urb_list\n"); + hci->td_array->len = 0; + + while (lh != list_lh) { + urb = list_entry (lh, struct urb, urb_list); + lh = lh->next; + if (((frame_number - urb->start_frame) & 0x7ff) < + urb->number_of_packets) { + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + if (((frame_number - + urb->start_frame) & 0x7ff) > 0x400) { + if (qu_urbstate (urb) > 0) + urb = qu_return_urb (hci, urb, 1); + else + urb = qu_next_urb (hci, urb, 1); + + if (lh == list_lh && urb) + lh = &urb->urb_list; + } + } + } + } + return 1; +} +#endif // if0 + +/*************************************************************************** + * Function Name : sh_scan_urb_list + * + * This function goes through the urb list and schedule the + * the transaction. + * + * Input: hci = data structure for the host controller + * list_lh = pointer to the isochronous list + * + * Return: 0 = unsuccessful; 1 = successful + **************************************************************************/ +static int sh_scan_urb_list (hci_t * hci, struct list_head *list_lh) +{ + struct list_head *lh = NULL; + struct urb *urb; + + if (list_lh == NULL) { + DBGERR ("sh_scan_urb_list: error, list_lh == NULL\n"); + } + + DBGFUNC ("enter sh_scan_urb_list: frame# \n"); + + list_for_each (lh, list_lh) { + urb = list_entry (lh, struct urb, urb_list); + if (urb == NULL) + return 1; + if (!usb_pipeint (urb->pipe) + || (((hci->frame_number - urb->start_frame) + & 0x7ff) >= urb->interval)) { + DBGVERBOSE ("sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, urb->interval, + usb_pipeint (urb->pipe)); + if (!sh_add_packet (hci, urb)) { + return 0; + } else { + DBGVERBOSE ("INT: start: %d fr_no: %d int: %d pint: %d\n", + urb->start_frame, hci->frame_number, + urb->interval, usb_pipeint (urb->pipe)); + urb->start_frame = hci->frame_number; + return 0; + + } + } + } + return 1; +} + +/*************************************************************************** + * Function Name : sh_shedule_trans + * + * This function schedule the USB transaction. + * This function will process the endpoint in the following order: + * interrupt, control, and bulk. + * + * Input: hci = data structure for the host controller + * isSOF = flag indicate if Start Of Frame has occurred + * + * Return: 0 + **************************************************************************/ +static int sh_schedule_trans (hci_t * hci, int isSOF) +{ + int units_left = 1; + struct list_head *lh; + + if (hci == NULL) { + DBGERR ("sh_schedule_trans: hci == NULL\n"); + return 0; + } + if (hci->td_array == NULL) { + DBGERR ("sh_schedule_trans: hci->td_array == NULL\n"); + return 0; + } + + if (hci->td_array->len != 0) { + DBGERR ("ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0\n", + hci->td_array->len); + } + + /* schedule the next available interrupt transfer or the next + * stage of the interrupt transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->intr_list)) { + units_left = sh_scan_urb_list (hci, &hci->intr_list); + } + + /* schedule the next available control transfer or the next + * stage of the control transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->ctrl_list) && units_left > 0) { + units_left = sh_scan_urb_list (hci, &hci->ctrl_list); + } + + /* schedule the next available bulk transfer or the next + * stage of the bulk transfer */ + + if (hci->td_array->len == 0 && !list_empty (&hci->bulk_list) && units_left > 0) { + sh_scan_urb_list (hci, &hci->bulk_list); + + /* be fair to each BULK URB (move list head around) + * only when the new SOF happens */ + + lh = hci->bulk_list.next; + list_del (&hci->bulk_list); + list_add (&hci->bulk_list, lh); + } + return 0; +} + +/*************************************************************************** + * Function Name : sh_add_packet + * + * This function forms the packet and transmit the packet. This function + * will handle all endpoint type: isochoronus, interrupt, control, and + * bulk. + * + * Input: hci = data structure for the host controller + * urb = USB request block data structure + * + * Return: 0 = unsucessful; 1 = successful + **************************************************************************/ +static int sh_add_packet (hci_t * hci, struct urb * urb) +{ + __u8 *data = NULL; + int len = 0; + int toggle = 0; + int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + int endpoint = usb_pipeendpoint (urb->pipe); + int address = usb_pipedevice (urb->pipe); + int slow = (((urb->pipe) >> 26) & 1); + int out = usb_pipeout (urb->pipe); + int pid = 0; + int ret; + int i = 0; + int iso = 0; + + DBGFUNC ("enter sh_add_packet\n"); + if (maxps == 0) + maxps = 8; + + /* calculate len, toggle bit and add the transaction */ + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + pid = out ? PID_OUT : PID_IN; + iso = 1; + i = hci->frame_number - urb->start_frame; + data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + len = urb->iso_frame_desc[i].length; + break; + + case PIPE_BULK: /* BULK and BULK0 */ + case PIPE_INTERRUPT: + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = usb_gettoggle (urb->dev, endpoint, out); + break; + + case PIPE_CONTROL: + switch (qu_urbstate (urb)) { + case US_CTRL_SETUP: + len = 8; + pid = PID_SETUP; + data = urb->setup_packet; + toggle = 0; + break; + + case US_CTRL_DATA: + if (!hci->last_packet_nak) { + /* The last packet received is not a nak: + * reset the nak count + */ + + hci->nakCnt = 0; + } + if (urb->transfer_buffer_length != 0) { + pid = out ? PID_OUT : PID_IN; + len = urb->transfer_buffer_length - urb->actual_length; + data = urb->transfer_buffer + urb->actual_length; + toggle = (urb->actual_length & maxps) ? 0 : 1; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } else { + /* correct state and fall through */ + qu_seturbstate (urb, US_CTRL_ACK); + } + + case US_CTRL_ACK: + len = 0; + + /* reply in opposite direction */ + pid = !out ? PID_OUT : PID_IN; + toggle = 1; + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + break; + } + } + + ret = + hc_add_trans (hci, len, data, toggle, maxps, slow, endpoint, + address, pid, iso, qu_urbstate (urb)); + + DBGVERBOSE ("transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x " + "max:%d\n len:%d ret:%d data:%p left:%d\n", + address, endpoint, pid, toggle, iso, slow, + maxps, len, ret, data, hci->hp.units_left); + + if (ret >= 0) { + hci->td_array->td[hci->td_array->len].urb = urb; + hci->td_array->td[hci->td_array->len].len = ret; + hci->td_array->td[hci->td_array->len].iso_index = i; + hci->td_array->len++; + hci->active_trans = 1; + return 1; + } + return 0; +} + +/*************************************************************************** + * Function Name : cc_to_error + * + * This function maps the SL811HS hardware error code to the linux USB error + * code. + * + * Input: cc = hardware error code + * + * Return: USB error code + **************************************************************************/ +static int cc_to_error (int cc) +{ + int errCode = 0; + if (cc & SL11H_STATMASK_ERROR) { + errCode |= -EILSEQ; + } else if (cc & SL11H_STATMASK_OVF) { + errCode |= -EOVERFLOW; + } else if (cc & SL11H_STATMASK_STALL) { + errCode |= -EPIPE; + } + return errCode; +} + +/*************************************************************************** + * Function Name : sh_done_list + * + * This function process the packet when it has done finish transfer. + * + * 1) It handles hardware error + * 2) It updates the URB state + * 3) If the USB transaction is complete, it start the return stack path. + * + * Input: hci = data structure for the host controller + * isExcessNak = flag tells if there excess NAK condition occurred + * + * Return: urb_state or -1 if the transaction has complete + **************************************************************************/ +static int sh_done_list (hci_t * hci, int *isExcessNak) +{ + int actbytes = 0; + int active = 0; + void *data = NULL; + int cc; + int maxps; + int toggle; + struct urb *urb; + int urb_state = 0; + int ret = 1; /* -1 parse abbort, 1 parse ok, 0 last element */ + int trans = 0; + int len; + int iso_index = 0; + int out; + int pid = 0; + int debugLen = 0; + + *isExcessNak = 0; + + DBGFUNC ("enter sh_done_list: td_array->len = 0x%x\n", + hci->td_array->len); + + debugLen = hci->td_array->len; + if (debugLen > 1) + DBGERR ("sh_done_list: td_array->len = 0x%x > 1\n", + hci->td_array->len); + + for (trans = 0; ret && trans < hci->td_array->len && trans < MAX_TRANS; + trans++) { + urb = hci->td_array->td[trans].urb; + /* FIXME: */ + /* +++ I'm sorry, can't handle NULL-Pointers 21.11.2002 (hne) */ + if (!urb) { + DBGERR ("sh_done_list: urb = NULL\n"); + continue; + } + if (!urb->dev || !urb->pipe) { + if (!urb->dev) DBGERR ("sh_done_list: urb->dev = NULL\n"); + if (!urb->pipe) DBGERR ("sh_done_list: urb->pipe = NULL\n"); + continue; + } + /* --- 21.11.2002 (hne) */ + + len = hci->td_array->td[trans].len; + out = usb_pipeout (urb->pipe); + + if (usb_pipeisoc (urb->pipe)) { + iso_index = hci->td_array->td[trans].iso_index; + data = urb->transfer_buffer + urb->iso_frame_desc[iso_index].offset; + toggle = 0; + } else { + data = urb->transfer_buffer + urb->actual_length; + /* +++ Crash (hne) urb->dev == NULL !!! */ + toggle = usb_gettoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)); + /* --- Crash (hne) urb->dev == NULL !!! */ + + } + urb_state = qu_urbstate (urb); + pid = out ? PID_OUT : PID_IN; + ret = hc_parse_trans (hci, &actbytes, data, &cc, &toggle, len, + pid, urb_state); + maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + + if (maxps == 0) + maxps = 8; + + active = (urb_state != US_CTRL_SETUP) && (actbytes && !(actbytes & (maxps - 1))); + + /* If the transfer is not bulk in, then it is necessary to get all + * data specify by the urb->transfer_len. + */ + + if (!(usb_pipebulk (urb->pipe) && usb_pipein (urb->pipe))) + active = active && (urb->transfer_buffer_length != urb->actual_length + actbytes); + + if (urb->transfer_buffer_length == urb->actual_length + actbytes) + active = 0; + + if ((cc & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | + SL11H_STATMASK_OVF | SL11H_STATMASK_STALL)) + && !(cc & SL11H_STATMASK_NAK)) { + if (++urb->error_count > 3) { + DBGERR ("done_list: excessive error: errcount = 0x%x, cc = 0x%x\n", + urb->error_count, cc); + urb_state = 0; + active = 0; + } else { + DBGERR ("done_list: packet err, cc=0x%x, " + " urb->length=0x%x, actual_len=0x%x," + " urb_state=0x%x\n", + cc, urb->transfer_buffer_length, + urb->actual_length, urb_state); +// if (cc & SL11H_STATMASK_STALL) { + /* The USB function is STALLED on a control pipe (0), + * then it needs to send the SETUP command again to + * clear the STALL condition + */ + +// if (usb_pipeendpoint (urb->pipe) == 0) { +// urb_state = 2; +// active = 0; +// } +// } else + active = 1; + } + } else { + if (cc & SL11H_STATMASK_NAK) { + if (hci->nakCnt < 0x10000) { + hci->nakCnt++; + hci->last_packet_nak = 1; + active = 1; + *isExcessNak = 0; + } else { + DBGERR ("done_list: nak count exceed limit\n"); + active = 0; + *isExcessNak = 1; + hci->nakCnt = 0; + } + } else { + hci->nakCnt = 0; + hci->last_packet_nak = 0; + } + + if (urb_state != US_CTRL_SETUP) { + /* no error */ + urb->actual_length += actbytes; + usb_settoggle (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe), toggle); + } + if (usb_pipeisoc (urb->pipe)) { + urb->iso_frame_desc[iso_index].actual_length = actbytes; + urb->iso_frame_desc[iso_index].status = cc_to_error (cc); + active = (iso_index < urb->number_of_packets); + } + } + if (!active) { + if (!urb_state) { + urb->status = cc_to_error (cc); + if (urb->status) { + DBGERR ("error on received packet: urb->status = 0x%x\n", + urb->status); + } + hci->td_array->len = 0; + qu_return_urb (hci, urb, 1); + return -1; + } else { + /* We do not want to decrement the urb_state if exceeded nak, + * because we need to finish the data stage of the control + * packet + */ + + if (!(*isExcessNak)) + urb_state--; + qu_seturbstate (urb, urb_state); + } + } + } + + if (urb_state < 0) + DBGERR ("ERROR: done_list, urb_state = %d, suppose > 0\n", + urb_state); + if (debugLen != hci->td_array->len) { + DBGERR ("ERROR: done_list, debugLen!= td_array->len," + "debugLen = 0x%x, hci->td_array->len = 0x%x\n", + debugLen, hci->td_array->len); + } + + hci->td_array->len = 0; + + return urb_state; +} diff -Nru a/drivers/usb/host/hc_simple.h b/drivers/usb/host/hc_simple.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/hc_simple.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,230 @@ +/*-------------------------------------------------------------------------*/ +/* list of all controllers using this driver + * */ + +static LIST_HEAD (hci_hcd_list); + +/* URB states (urb_state) */ +/* isoc, interrupt single state */ + +/* bulk transfer main state and 0-length packet */ +#define US_BULK 0 +#define US_BULK0 1 +/* three setup states */ +#define US_CTRL_SETUP 2 +#define US_CTRL_DATA 1 +#define US_CTRL_ACK 0 + +/*-------------------------------------------------------------------------*/ +/* HC private part of a device descriptor + * */ + +#define NUM_EDS 32 + +typedef struct epd { + struct urb *pipe_head; + struct list_head urb_queue; +// int urb_state; + struct timer_list timeout; + int last_iso; /* timestamp of last queued ISOC transfer */ + +} epd_t; + +struct hci_device { + epd_t ed[NUM_EDS]; +}; + +/*-------------------------------------------------------------------------*/ +/* Virtual Root HUB + */ + +#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv) + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; /* interrupt URB of root hub */ + int send; /* active flag */ + int interval; /* intervall of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ +}; + +#if 1 +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#endif + +/*-------------------------------------------------------------------------*/ +/* struct for each HC + */ + +#define MAX_TRANS 32 + +typedef struct td { + struct urb *urb; + __u16 len; + __u16 iso_index; +} td_t; + +typedef struct td_array { + int len; + td_t td[MAX_TRANS]; +} td_array_t; + +typedef struct hci { + struct virt_root_hub rh; /* roothub */ + wait_queue_head_t waitq; /* deletion of URBs and devices needs a waitqueue */ + int active; /* HC is operating */ + + struct list_head ctrl_list; /* set of ctrl endpoints */ + struct list_head bulk_list; /* set of bulk endpoints */ + struct list_head iso_list; /* set of isoc endpoints */ + struct list_head intr_list; /* ordered (tree) set of int endpoints */ + struct list_head del_list; /* set of entpoints to be deleted */ + + td_array_t *td_array; + td_array_t a_td_array; + td_array_t i_td_array[2]; + + struct list_head hci_hcd_list; /* list of all hci_hcd */ + struct usb_bus *bus; /* our bus */ + +// int trans; /* number of transactions pending */ + int active_urbs; + int active_trans; + int frame_number; /* frame number */ + hcipriv_t hp; /* individual part of hc type */ + int nakCnt; + int last_packet_nak; + +} hci_t; + +/*-------------------------------------------------------------------------*/ +/* condition (error) CC codes and mapping OHCI like + */ + +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +/* urb interface functions */ +static int hci_get_current_frame_number (struct usb_device *usb_dev); +static int hci_unlink_urb (struct urb * urb); + +static int qu_queue_urb (hci_t * hci, struct urb * urb); + +/* root hub */ +static int rh_init_int_timer (struct urb * urb); +static int rh_submit_urb (struct urb * urb); +static int rh_unlink_urb (struct urb * urb); + +/* schedule functions */ +static int sh_add_packet (hci_t * hci, struct urb * urb); + +/* hc specific functions */ +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len); +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state); +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state); + +static void hc_start_int (hci_t * hci); +static void hc_stop_int (hci_t * hci); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void urb_print (struct urb * urb, char *str, int small) +{ + unsigned int pipe = urb->pipe; + int i, len; + + if (!urb->dev || !urb->dev->bus) { + dbg ("%s URB: no dev", str); + return; + } + + printk ("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)\n", + str, hci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), usb_pipeendpoint (pipe), + usb_pipeout (pipe) ? 'O' : 'I', + usb_pipetype (pipe) < 2 ? (usb_pipeint (pipe) ? "INTR" : "ISOC") + : (usb_pipecontrol (pipe) ? "CTRL" : "BULK"), urb->transfer_flags, + urb->actual_length, urb->transfer_buffer_length, urb->status, + urb->status); + if (!small) { + if (usb_pipecontrol (pipe)) { + printk (__FILE__ ": cmd(8):"); + for (i = 0; i < 8; i++) + printk (" %02x", ((__u8 *) urb->setup_packet)[i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (__FILE__ ": data(%d/%d):", urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe) ? urb-> transfer_buffer_length : urb->actual_length; + for (i = 0; i < 2096 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer)[i]); + printk ("%s stat:%d\n", i < len ? "..." : "", + urb->status); + } + } +} diff -Nru a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/hc_sl811.c Fri Oct 24 17:01:14 2003 @@ -0,0 +1,1484 @@ +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS USB HCD for Linux Version 0.1 (10/28/2001) 0.3 (11/23/2003) + * + * requires (includes) hc_simple.[hc] simple generic HCD frontend + * + * COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC. + * + * ! This driver have end of live! Please use hcd/sl811.c instand ! + * + * 05.06.2003 HNE + * Support x86 architecture now. + * Set "bus->bus_name" at init. + * hc_reset,regTest: Don't load driver, if pattern-Test failed (better error returns) + * + * 06.06.2003 HNE + * Moved regTest from hc_reset to hc_found_hci. So we check registers + * only at start, and not at unload. Only all Register show, if full Debug. + * + * 22.09.2003 HNE + * Do not write SL11H_INTSTATREG in loop, use delay instand. + * If device disconnected, wait only for next insert interrupt (no Idle-Interrpts). + * + * 29.09.2003 HNE + * Moving hc_sl811-arm.h and hc_sl811-x86.h to include/asm-.../hc_sl811-hw.h + * + * 03.10.2003 HNE + * Low level only for port io into hardware-include. + * GPRD as parameter (ARM only). + + * ToDo: + * Separate IO-Base for second controller (see sl811.c) + * Only as module tested! Compiled in Version not tested! + * + *-------------------------------------------------------------------------* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MODNAME "HC_SL811" + +#undef HC_URB_TIMEOUT +#undef HC_SWITCH_INT +#undef HC_ENABLE_ISOC + +// #define SL811_DEBUG +#define SL811_DEBUG_ERR +// #define SL811_DEBUG_IRQ +// #define SL811_DEBUG_VERBOSE + +#ifdef SL811_DEBUG_ERR +#define DBGERR(fmt, args...) printk(fmt,## args) +#else +#define DBGERR(fmt, args...) +#endif + +#ifdef SL811_DEBUG +#define DBG(fmt, args...) printk(fmt,## args) +#else +#define DBG(fmt, args...) +#endif + +#ifdef SL811_DEBUG_FUNC +#define DBGFUNC(fmt, args...) printk(fmt,## args) +#else +#define DBGFUNC(fmt, args...) +#endif + +#ifdef SL811_DEBUG_DATA +#define DBGDATAR(fmt, args...) printk(fmt,## args) +#define DBGDATAW(fmt, args...) printk(fmt,## args) +#else +#define DBGDATAR(fmt, args...) +#define DBGDATAW(fmt, args...) +#endif + +#ifdef SL811_DEBUG_VERBOSE +#define DBGVERBOSE(fmt, args...) printk(fmt,## args) +#else +#define DBGVERBOSE(fmt, args...) +#endif + +#define TRUE 1 +#define FALSE 0 + +#define HC_SWITCH_INT +#include "hc_sl811.h" +#include "hc_simple.h" + +static int urb_debug = 0; + +#include "hc_simple.c" +#include "hc_sl811_rh.c" + +/* Include hardware and board depens */ +#include + +/* forware declaration */ +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state); + +static int sofWaitCnt = 0; + +MODULE_PARM (urb_debug, "i"); +MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); + +static int hc_reset (hci_t * hci); + +/*************************************************************************** + * Function Name : SL811Read + * + * Read a byte of data from the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * + * Return: data + **************************************************************************/ + +static __u8 inline SL811Read (hci_t *hci, __u8 offset) +{ + hcipriv_t *hp = &hci->hp; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811Read: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + return (sl811_read_data (hp)); +} + +/*************************************************************************** + * Function Name : SL811Write + * + * Write a byte of data to the SL811H/SL11H + * + * Input: hci = data structure for the host controller + * offset = address of SL811/SL11H register or memory + * data = the data going to write to SL811H + * + * Return: none + **************************************************************************/ + +static void inline SL811Write (hci_t *hci, __u8 offset, __u8 data) +{ + hcipriv_t *hp = &hci->hp; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811Write: Error port not set!\n"); +#endif + sl811_write_index_data (hp, offset, data); +} + + +/*************************************************************************** + * Function Name : SL811BufRead + * + * Read consecutive bytes of data from the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the buffer where the data will store + * size = number of bytes to read + * + * Return: none + **************************************************************************/ + +static void SL811BufRead (hci_t *hci, __u8 offset, __u8 *buf, __u8 size) +{ + hcipriv_t *hp = &hci->hp; + if( size <= 0) + return; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811BufRead: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + DBGDATAR ("SL811BufRead: io=%X offset = %02x, data = ", hp->hcport, offset); + while (size--) { + *buf++ = sl811_read_data(hp); + DBGDATAR ("%02x ", *(buf-1)); + } + DBGDATAR ("\n"); +} + +/*************************************************************************** + * Function Name : SL811BufWrite + * + * Write consecutive bytes of data to the SL811H/SL11H buffer + * + * Input: hci = data structure for the host controller + * offset = SL811/SL11H register offset + * buf = the data buffer + * size = number of bytes to write + * + * Return: none + **************************************************************************/ + +static void SL811BufWrite(hci_t *hci, __u8 offset, __u8 *buf, __u8 size) +{ + hcipriv_t *hp = &hci->hp; + if(size<=0) + return; +#ifdef SL811_DEBUG_ERR + if (!hp->hcport) + DBGERR ("SL811BufWrite: Error port not set!\n"); +#endif + sl811_write_index (hp, offset); + DBGDATAW ("SL811BufWrite: io=%X offset = %02x, data = ", hp->hcport, offset); + while (size--) { + DBGDATAW ("%02x ", *buf); + sl811_write_data (hp, *buf); + buf++; + } + DBGDATAW ("\n"); +} + +/*************************************************************************** + * Function Name : regTest + * + * This routine test the Read/Write functionality of SL811HS registers + * + * 1) Store original register value into a buffer + * 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255) + * 3) Read from register + * 4) Compare the written value with the read value and make sure they are + * equivalent + * 5) Restore the original register value + * + * Input: hci = data structure for the host controller + * + * + * Return: TRUE = passed; FALSE = failed + **************************************************************************/ +int regTest (hci_t * hci) +{ + int i, result = TRUE; + __u8 buf[256], data; + + DBGFUNC ("Enter regTest\n"); + for (i = 0x10; i < 256; i++) { + /* save the original buffer */ + buf[i] = SL811Read (hci, i); + + /* Write the new data to the buffer */ + SL811Write (hci, i, i); + } + + /* compare the written data */ + for (i = 0x10; i < 256; i++) { + data = SL811Read (hci, i); + if (data != i) { + DBGERR ("Pattern test failed!! value = 0x%x, s/b 0x%x\n", + data, i); + result = FALSE; + + /* If no Debug, show only first failed Address */ + if (!urb_debug) + break; + } + } + + /* restore the data */ + for (i = 0x10; i < 256; i++) { + SL811Write (hci, i, buf[i]); + } + + return (result); +} + +#if 0 /* unused (hne) */ +/*************************************************************************** + * Function Name : regShow + * + * Display all SL811HS register values + * + * Input: hci = data structure for the host controller + * + * Return: none + **************************************************************************/ +static void regShow (hci_t * hci) +{ + int i; + for (i = 0; i < 256; i++) { + printk ("offset %d: 0x%x\n", i, SL811Read (hci, i)); + } +} +#endif // if0 + +/************************************************************************ + * Function Name : USBReset + * + * This function resets SL811HS controller and detects the speed of + * the connecting device + * + * Input: hci = data structure for the host controller + * + * Return: 0 = no device attached; 1 = USB device attached + * + ***********************************************************************/ +/* [2.4.22] sl811_hc_reset */ +static int USBReset (hci_t * hci) +{ + int status; + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("enter USBReset\n"); + + SL811Write (hci, SL11H_CTLREG2, 0xae); + + // setup master and full speed + + SL811Write (hci, SL11H_CTLREG1, 0x08); // reset USB + mdelay (20); // 20ms + SL811Write (hci, SL11H_CTLREG1, 0); // remove SE0 + + /* disable all interrupts (18.09.2003) */ + SL811Write (hci, SL11H_INTENBLREG, 0); + + /* 19.09.2003 [2.4.22] */ + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); // clear all interrupt bits + status = SL811Read (hci, SL11H_INTSTATREG); + + if (status & SL11H_INTMASK_USBRESET) // Check if device is removed (0x40) + { + DBG ("USBReset: Device removed %03X\n", hci->hp.hcport); + /* 19.09.2003 [2.4.22] only IRQ for insert... + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + */ + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_INSRMV); + hp->RHportStatus->portStatus &= + ~(PORT_CONNECT_STAT | PORT_ENABLE_STAT); + + return 0; + } + + // Send SOF to address 0, endpoint 0. + SL811Write (hci, SL11H_BUFLNTHREG_B, 0); //zero lenth + SL811Write (hci, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 + SL811Write (hci, SL11H_DEVADDRREG_B, 0x01); //address0 + SL811Write (hci, SL11H_SOFLOWREG, 0xe0); + + if (!(status & SL11H_INTMASK_DSTATE)) { /* 0x80 */ + /* slow speed device connect directly to root-hub */ + + DBG ("USBReset: low speed Device attached %03X\n", hci->hp.hcport); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + // SL811Write (hci, SL11H_SOFTMRREG, 0xee); /* the same, but better reading (hne) */ + SL811Write (hci, SL11H_CTLREG2, 0xee); + SL811Write (hci, SL11H_CTLREG1, 0x21); + + hp->RHportStatus->portStatus |= + (PORT_CONNECT_STAT | PORT_LOW_SPEED_DEV_ATTACH_STAT); + + } else { + /* full speed device connect directly to root hub */ + + DBG ("USBReset: full speed Device attached %03X \n", hci->hp.hcport); + SL811Write (hci, SL11H_CTLREG1, 0x8); + mdelay (20); + // SL811Write (hci, SL11H_SOFTMRREG, 0xae); /* the same, but better reading (hne) */ + SL811Write (hci, SL11H_CTLREG2, 0xae); + SL811Write (hci, SL11H_CTLREG1, 0x01); + + hp->RHportStatus->portStatus |= (PORT_CONNECT_STAT); + hp->RHportStatus->portStatus &= ~PORT_LOW_SPEED_DEV_ATTACH_STAT; + + } + + /* start the SOF or EOP */ + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x01); + + /* clear all interrupt bits */ + /* 19.09.2003 [2.4.22] */ + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + /* enable all interrupts */ + SL811Write (hci, SL11H_INTENBLREG, + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV); + + return 1; +} + +/*-------------------------------------------------------------------------*/ +/* tl functions */ +static inline void hc_mark_last_trans (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u8 *ptd = hp->tl; + + dbg ("enter hc_mark_last_trans\n"); + if (ptd == NULL) { + printk ("hc_mark_last_trans: ptd = null\n"); + return; + } + if (hp->xferPktLen > 0) + *(ptd + hp->tl_last) |= (1 << 3); +} + +static inline void hc_flush_data_cache (hci_t * hci, void *data, int len) +{ +} + +/************************************************************************ + * Function Name : hc_add_trans + * + * This function sets up the SL811HS register and transmit the USB packets. + * + * 1) Determine if enough time within the current frame to send the packet + * 2) Load the data into the SL811HS register + * 3) Set the appropriate command to the register and trigger the transmit + * + * Input: hci = data structure for the host controller + * len = data length + * data = transmitting data + * toggle = USB toggle bit, either 0 or 1 + * maxps = maximum packet size for this endpoint + * slow = speed of the device + * endpoint = endpoint number + * address = USB address of the device + * pid = packet ID + * format = + * urb_state = the current stage of USB transaction + * + * Return: 0 = no time left to schedule the transfer + * 1 = success + * + ***********************************************************************/ +static inline int hc_add_trans (hci_t * hci, int len, void *data, int toggle, + int maxps, int slow, int endpoint, int address, + int pid, int format, int urb_state) +{ + hcipriv_t *hp = &hci->hp; + __u16 speed; + int ii, jj, kk; + + DBGFUNC ("enter hc_add_trans: len=0x%x, toggle:0x%x, endpoing:0x%x," + " addr:0x%x, pid:0x%x,format:0x%x\n", len, toggle, endpoint, + address, pid, format); + + if (len > maxps) { + len = maxps; + } + + speed = hp->RHportStatus->portStatus; + if (speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 1024; + } else { + if (slow) { +// ii = (8*7*8 + 6*3) * len + 800; + ii = 8 * 8 * len + 2048; + } else +// ii = (8*7 + 6*3)*len + 110; + ii = 8 * len + 256; + } + + ii += 2 * 10 * len; + + jj = SL811Read (hci, SL11H_SOFTMRREG); + + /* Read back SOF counter HIGH (bit0-bit5 only) 26.11.2002 (hne) */ + // kk = (jj & 0xFF) * 64 - ii; + kk = (jj & (64-1)) * 64 - ii; + + if (kk < 0) { + DBGVERBOSE + ("hc_add_trans: no bandwidth for schedule, ii = 0x%x," + "jj = 0x%x, len =0x%x, active_trans = 0x%x\n", ii, jj, len, + hci->active_trans); + return (-1); + } + + if (pid != PID_IN) { + /* Load data into hc */ + + SL811BufWrite (hci, SL11H_DATA_START, (__u8 *) data, len); + } + + /* transmit */ + + SL11StartXaction (hci, (__u8) address, (__u8) endpoint, (__u8) pid, len, + toggle, slow, urb_state); + + return len; +} + +/************************************************************************ + * Function Name : hc_parse_trans + * + * This function checks the status of the transmitted or received packet + * and copy the data from the SL811HS register into a buffer. + * + * 1) Check the status of the packet + * 2) If successful, and IN packet then copy the data from the SL811HS register + * into a buffer + * + * Input: hci = data structure for the host controller + * actbytes = pointer to actual number of bytes + * data = data buffer + * cc = packet status + * length = the urb transmit length + * pid = packet ID + * urb_state = the current stage of USB transaction + * + * Return: 0 + ***********************************************************************/ +static inline int hc_parse_trans (hci_t * hci, int *actbytes, __u8 * data, + int *cc, int *toggle, int length, int pid, + int urb_state) +{ + __u8 addr; + __u8 len; + __u8 pkt_stat; + + DBGFUNC ("enter hc_parse_trans\n"); + + /* get packet status; convert ack rcvd to ack-not-rcvd */ + + *cc = pkt_stat \ + = SL811Read (hci, SL11H_PKTSTATREG); + + if (pkt_stat & + (SL11H_STATMASK_ERROR | SL11H_STATMASK_TMOUT | SL11H_STATMASK_OVF | + SL11H_STATMASK_NAK | SL11H_STATMASK_STALL)) { + if (*cc & SL11H_STATMASK_OVF) + DBGERR ("parse trans: error recv ack, cc = 0x%x/0x%x, TX_BASE_Len = " + "0x%x, TX_count=0x%x\n", pkt_stat, + SL811Read (hci, SL11H_PKTSTATREG), + SL811Read (hci, SL11H_BUFLNTHREG), + SL811Read (hci, SL11H_XFERCNTREG)); + else + DBGVERBOSE ("parse trans: error recv ack, cc = 0x%x/0x%x\n", + pkt_stat, SL811Read (hci, SL11H_PKTSTATREG)); + } else { + DBGVERBOSE ("parse trans: recv ack, cc=0x%x, len=0x%x, pid=0x%x, urb=%d\n", + pkt_stat, length, pid, urb_state); + + /* Successful data */ + if ((pid == PID_IN) && (urb_state != US_CTRL_SETUP)) { + + /* Find the base address */ + addr = SL811Read (hci, SL11H_BUFADDRREG); + + /* Find the Transmit Length */ + len = SL811Read (hci, SL11H_BUFLNTHREG); + + /* The actual data length = xmit length reg - xfer count reg */ + *actbytes = len - SL811Read (hci, SL11H_XFERCNTREG); + + if ((data != NULL) && (*actbytes > 0)) { + SL811BufRead (hci, addr, data, *actbytes); + + } else if ((data == NULL) && (*actbytes <= 0)) { + DBGERR ("hc_parse_trans: data = NULL or actbyte = 0x%x\n", + *actbytes); + return 0; + } + } else if (pid == PID_OUT) { + *actbytes = length; + } else { + // printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid); + } + *toggle = !*toggle; + } + + return 0; +} + +/************************************************************************ + * Function Name : hc_start_int + * + * This function enables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_start_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + int mask = + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR | + SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET; + SL811Write (hci, IntEna, mask); +#endif +} + +/************************************************************************ + * Function Name : hc_stop_int + * + * This function disables SL811HS interrupts + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +static void hc_stop_int (hci_t * hci) +{ +#ifdef HC_SWITCH_INT + SL811Write (hci, SL11H_INTSTATREG, 0xff); +// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); + +#endif +} + +/************************************************************************ + * Function Name : handleInsRmvIntr + * + * This function handles the insertion or removal of device on SL811HS. + * It resets the controller and updates the port status + * + * Input: hci = data structure for the host controller + * + * Return: none + ***********************************************************************/ +void handleInsRmvIntr (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + USBReset (hci); + + /* Changes in connection status */ + + hp->RHportStatus->portChange |= PORT_CONNECT_CHANGE; + + /* Port Enable or Disable */ + + if (hp->RHportStatus->portStatus & PORT_CONNECT_STAT) { + /* device is connected to the port: + * 1) Enable port + * 2) Resume ?? + */ +// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + /* Over Current is not supported by the SL811 HW ?? */ + + /* How about the Port Power ?? */ + + } else { + /* Device has disconnect: + * 1) Disable port + */ + + hp->RHportStatus->portStatus &= ~(PORT_ENABLE_STAT); + hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE; + + } +} + +/***************************************************************** + * + * Function Name: SL11StartXaction + * + * This functions load the registers with appropriate value and + * transmit the packet. + * + * Input: hci = data structure for the host controller + * addr = USB address of the device + * epaddr = endpoint number + * pid = packet ID + * len = data length + * toggle = USB toggle bit, either 0 or 1 + * slow = speed of the device + * urb_state = the current stage of USB transaction + * + * Return: 0 = error; 1 = successful + * + *****************************************************************/ +int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, + int toggle, int slow, int urb_state) +{ + + hcipriv_t *hp = &hci->hp; + __u8 cmd = 0; + __u8 setup_data[4]; + __u16 speed; + + speed = hp->RHportStatus->portStatus; + if (!(speed & PORT_LOW_SPEED_DEV_ATTACH_STAT) && slow) { + cmd |= SL11H_HCTLMASK_PREAMBLE; + } + switch (pid) { + case PID_SETUP: + // cmd &= SL11H_HCTLMASK_PREAMBLE; /* 26.11.2002 (hne) */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP + | SL11H_HCTLMASK_WRITE; + DBGVERBOSE ("SL811 Xaction: SETUP cmd=%02X\n", cmd); + break; + + case PID_OUT: + // cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); /* (hne) 26.11.2002 */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP + | SL11H_HCTLMASK_WRITE; + if (toggle) { + cmd |= SL11H_HCTLMASK_SEQ; + } + DBGVERBOSE ("SL811 Xaction: OUT cmd=%02X\n", cmd); + break; + + case PID_IN: + // cmd &= (SL11H_HCTLMASK_SEQ | SL11H_HCTLMASK_PREAMBLE); /* (hne) 26.11.2002 */ + cmd |= SL11H_HCTLMASK_ARM + | SL11H_HCTLMASK_ENBLEP; + DBGVERBOSE ("SL811 Xaction: IN cmd=%02x\n", cmd); + break; + + default: + DBGERR ("ERR: SL11StartXaction: unknow pid = 0x%x\n", pid); + return 0; + } + setup_data[0] = SL11H_DATA_START; /* 01:SL11H_BUFADDRREG */ + setup_data[1] = len; /* 02:SL11H_BUFLNTHREG */ + setup_data[2] = (((pid & 0x0F) << 4) | (epaddr & 0xF)); /* 03:SL11H_PIDEPREG */ + setup_data[3] = addr & 0x7F; /* 04:SL11H_DEVADDRREG */ + + SL811BufWrite (hci, SL11H_BUFADDRREG, (__u8 *) & setup_data[0], 4); + + // SL811Write (hci, SL11H_PIDEPREG, cmd); /* 03: grrr (hne) */ + SL811Write (hci, SL11H_HOSTCTLREG, cmd); /* 00: 26.11.2002 (hne) */ + +#if 0 + /* The SL811 has a hardware flaw when hub devices sends out + * SE0 between packets. It has been found in a TI chipset and + * cypress hub chipset. It causes the SL811 to hang + * The workaround is to re-issue the preample again. + */ + + if ((cmd & SL11H_HCTLMASK_PREAMBLE)) { + SL811Write (hci, SL11H_PIDEPREG_B, 0xc0); + SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable + } +#endif + return 1; +} + +/***************************************************************** + * + * Function Name: hc_interrupt + * + * Interrupt service routine. + * + * 1) determine the causes of interrupt + * 2) clears all interrupts + * 3) calls appropriate function to service the interrupt + * + * Input: irq = interrupt line associated with the controller + * hci = data structure for the host controller + * r = holds the snapshot of the processor's context before + * the processor entered interrupt code. (not used here) + * + * Return value : None. + * + *****************************************************************/ +static void hc_interrupt (int irq, void *__hci, struct pt_regs *r) +{ + __u8 ii; + hci_t *hci = __hci; + int isExcessNak = 0; + int urb_state = 0; + // __u8 tmpIrq = 0; + int irq_loop = 16; /* total irq handled at one hardware irq */ + +#ifdef SL811_DEBUG_IRQ + hcipriv_t *hp = &hci->hp; + unsigned char sta1, sta2; + + outb (SL11H_INTSTATREG, 0x220); // Interrupt-Status register, controller1 + sta1 = (__u8) inb (0x220+1); + + outb (SL11H_INTSTATREG, 0x222); // Interrupt-Status register, controller2 + sta2 = (__u8) inb (0x222+1); +#endif + + do { + /* Get value from interrupt status register */ + + ii = SL811Read (hci, SL11H_INTSTATREG); + + /* All interrupts handled? (hne) */ + if ( !(ii & (SL11H_INTMASK_INSRMV /* | SL11H_INTMASK_USBRESET */ | + SL11H_INTMASK_XFERDONE | SL11H_INTMASK_SOFINTR)) ) + { +#ifdef SL811_DEBUG_IRQ + // printk ("%Xh IRQ ista=%02X not me\n", hp->hcport, ii); + + if ( sta1 != 0x80 && sta1 != 0x90 && + sta2 != 0x80 && sta2 != 0x90 ) + printk ("%Xh IRQ sta=%02X,%02X\n", hp->hcport, sta1, sta2); +#endif + return; + } + + /* Interrupt will be handle now (18.09.2003 2.4.22) */ + SL811Write (hci, SL11H_INTSTATREG, 0xff); + + /* SOF-outputs are to slow. No debug any SOF */ + if ( !(ii & SL11H_INTMASK_SOFINTR) ) + DBGVERBOSE ("SL811 ISR: %s%s%s%s io=%03X\n", + (ii & SL11H_INTMASK_XFERDONE) ? " DONE": "", + (ii & SL11H_INTMASK_SOFINTR) ? " SOFINTR": "", + (ii & SL11H_INTMASK_INSRMV) ? " INSRMV": "", + (ii & SL11H_INTMASK_USBRESET) ? " USBRESET": "", + hci->hp.hcport ); + + // if (ii & (SL11H_INTMASK_INSRMV | SL11H_INTMASK_USBRESET)) { + // Found in 2.5.75 (19.09.2003) + // "SL11H_INTMASK_USBRESET" is always on, if no device connected! + if (ii & SL11H_INTMASK_INSRMV) { + /* Device insertion or removal detected for the USB port */ + /* Disable all interrupts */ + SL811Write (hci, SL11H_INTENBLREG, 0); + /* No SOF, Full speed */ + SL811Write (hci, SL11H_CTLREG1, 0); + + mdelay (100); // wait for device stable + handleInsRmvIntr (hci); + return; + } + + /* Clear all interrupts */ + // SL811Write (hci, SL11H_INTSTATREG, 0xff); + + if (ii & SL11H_INTMASK_XFERDONE) { + + /* USB Done interrupt occurred */ + // DBGVERBOSE ("xsta=%02X\n", SL811Read (hci, SL11H_PKTSTATREG)); + + urb_state = sh_done_list (hci, &isExcessNak); +#ifdef WARNING + if (hci->td_array->len > 0) + printk ("WARNING: IRQ, td_array->len = 0x%x, s/b:0\n", + hci->td_array->len); +#endif + if (hci->td_array->len == 0 && !isExcessNak + && !(ii & SL11H_INTMASK_SOFINTR) && (urb_state == 0)) { + if (urb_state == 0) { + /* All urb_state has not been finished yet! + * continue with the current urb transaction + */ + + if (hci->last_packet_nak == 0) { + if (!usb_pipecontrol + (hci->td_array->td[0].urb->pipe)) + sh_add_packet (hci, hci->td_array-> td[0].urb); + } + } else { + /* The last transaction has completed: + * schedule the next transaction + */ + + sh_schedule_trans (hci, 0); + } + } + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + return; + } + + if (ii & SL11H_INTMASK_SOFINTR) { + hci->frame_number = (hci->frame_number + 1) % 2048; + if (hci->td_array->len == 0) + sh_schedule_trans (hci, 1); + else { + if (sofWaitCnt++ > 100) { + /* The last transaction has not completed. + * Need to retire the current td, and let + * it transmit again later on. + * (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER + * GET TO THIS POINT) + */ + + DBGERR ("SOF interrupt: td_array->len = 0x%x, s/b:0 io=%03X\n", + hci->td_array->len, + hci->hp.hcport ); + urb_print (hci->td_array->td[hci->td_array->len - 1].urb, + "INTERRUPT", 0); + /* FIXME: Here sh_done_list was call with urb->dev=NULL 21.11.2002 (hne) */ + sh_done_list (hci, &isExcessNak); + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + hci->td_array->len = 0; + sofWaitCnt = 0; + } + } + +#if 0 /* grrr! This READ clears my XFERDONE interrupt! Its better handle this in a loop. (hne) */ + tmpIrq = SL811Read (hci, SL11H_INTSTATREG) & SL811Read (hci, SL11H_INTENBLREG); + if (tmpIrq) { + DBG ("IRQ occurred while service SOF: irq = 0x%x\n", + tmpIrq); + + /* If we receive a DONE IRQ after schedule, need to + * handle DONE IRQ again + */ + + if (tmpIrq & SL11H_INTMASK_XFERDONE) { + DBGERR ("XFERDONE occurred while service SOF: irq = 0x%x\n", + tmpIrq); + urb_state = sh_done_list (hci, &isExcessNak); + } + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } +#endif + } else { + DBG ("SL811 ISR: unknown, int=0x%x io=%03X\n", ii, hci->hp.hcport); + return; + } + + /* +++ (hne) + SL811Write (hci, SL11H_INTSTATREG, 0xff); + --- */ + + /* loop, if any interrupts can read (hne) */ + } while (--irq_loop); + + return; +} + +/***************************************************************** + * + * Function Name: hc_reset + * + * This function does register test and resets the SL811HS + * controller. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_reset (hci_t * hci) +{ + int attachFlag = 0; + + DBGFUNC ("Enter hc_reset\n"); + attachFlag = USBReset (hci); + if (attachFlag) { + setPortChange (hci, PORT_CONNECT_CHANGE); + } + return (0); +} + +/***************************************************************** + * + * Function Name: hc_alloc_trans_buffer + * + * This function allocates all transfer buffer + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_alloc_trans_buffer (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + int maxlen; + + hp->itl0_len = 0; + hp->itl1_len = 0; + hp->atl_len = 0; + + hp->itl_buffer_len = 1024; + hp->atl_buffer_len = 4096 - 2 * hp->itl_buffer_len; /* 2048 */ + + maxlen = (hp->itl_buffer_len > hp->atl_buffer_len) ? hp->itl_buffer_len : hp->atl_buffer_len; + + hp->tl = kmalloc (maxlen, GFP_KERNEL); + + if (!hp->tl) + return -ENOMEM; + + memset (hp->tl, 0, maxlen); + return 0; +} + +/***************************************************************** + * + * Function Name: getPortStatusAndChange + * + * This function gets the ports status from SL811 and format it + * to a USB request format + * + * Input: hci = data structure for the host controller + * + * Return value : port status and change + * + *****************************************************************/ +static __u32 getPortStatusAndChange (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + __u32 portstatus; + + DBGFUNC ("enter getPorStatusAndChange\n"); + + portstatus = hp->RHportStatus->portChange << 16 | hp->RHportStatus->portStatus; + + return (portstatus); +} + +/***************************************************************** + * + * Function Name: setPortChange + * + * This function set the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + + switch (bitPos) { + case PORT_CONNECT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_ENABLE_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + + case PORT_OVER_CURRENT_STAT: + hp->RHportStatus->portChange |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortChange + * + * This function clear the bit position of portChange. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortChange (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_CONNECT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_ENABLE_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_RESET_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_SUSPEND_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + + case PORT_OVER_CURRENT_CHANGE: + hp->RHportStatus->portChange &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: clrPortStatus + * + * This function clear the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void clrPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus &= ~bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: setPortStatus + * + * This function set the bit position of portStatus. + * + * Input: hci = data structure for the host controller + * bitPos = the bit position + * + * Return value : none + * + *****************************************************************/ +static void setPortStatus (hci_t * hci, __u16 bitPos) +{ + hcipriv_t *hp = &hci->hp; + switch (bitPos) { + case PORT_ENABLE_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_RESET_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_POWER_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + + case PORT_SUSPEND_STAT: + hp->RHportStatus->portStatus |= bitPos; + break; + } +} + +/***************************************************************** + * + * Function Name: hc_start + * + * This function starts the root hub functionality. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static int hc_start (hci_t * hci) +{ + DBGFUNC ("Enter hc_start\n"); + + rh_connect_rh (hci); + + return 0; +} + +/***************************************************************** + * + * Function Name: hc_alloc_hci + * + * This function allocates all data structure and store in the + * private data structure. + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static hci_t *__devinit hc_alloc_hci (void) +{ + hci_t *hci; + hcipriv_t *hp; + portstat_t *ps; + struct usb_bus *bus; + + DBGFUNC ("Enter hc_alloc_hci\n"); + hci = (hci_t *) kmalloc (sizeof (hci_t), GFP_KERNEL); + if (!hci) + return NULL; + + memset (hci, 0, sizeof (hci_t)); + + hp = &hci->hp; + + hp->irq = -1; + hp->hcport = -1; + + /* setup root hub port status */ + + ps = (portstat_t *) kmalloc (sizeof (portstat_t), GFP_KERNEL); + + if (!ps) + return NULL; + ps->portStatus = PORT_STAT_DEFAULT; + ps->portChange = PORT_CHANGE_DEFAULT; + hp->RHportStatus = ps; + + hci->nakCnt = 0; + hci->last_packet_nak = 0; + + hci->a_td_array.len = 0; + hci->i_td_array[0].len = 0; + hci->i_td_array[1].len = 0; + hci->td_array = &hci->a_td_array; + hci->active_urbs = 0; + hci->active_trans = 0; + INIT_LIST_HEAD (&hci->hci_hcd_list); + list_add (&hci->hci_hcd_list, &hci_hcd_list); + init_waitqueue_head (&hci->waitq); + + INIT_LIST_HEAD (&hci->ctrl_list); + INIT_LIST_HEAD (&hci->bulk_list); + INIT_LIST_HEAD (&hci->iso_list); + INIT_LIST_HEAD (&hci->intr_list); + INIT_LIST_HEAD (&hci->del_list); + + bus = usb_alloc_bus (&hci_device_operations); + if (!bus) { + kfree (hci); + return NULL; + } + + hci->bus = bus; + bus->bus_name = "sl811"; + bus->hcpriv = (void *) hci; + + return hci; +} + +/***************************************************************** + * + * Function Name: hc_release_hci + * + * This function De-allocate all resources + * + * Input: hci = data structure for the host controller + * + * Return value : 0 + * + *****************************************************************/ +static void hc_release_hci (hci_t * hci) +{ + hcipriv_t *hp = &hci->hp; + + DBGFUNC ("Enter hc_release_hci\n"); + + /* disconnect all devices */ + if (hci->bus->root_hub) + usb_disconnect (&hci->bus->root_hub); + + hc_reset (hci); + + if (hp->hcport) { + // Disable all Interrupts + SL811Write (hci, SL11H_INTENBLREG, 0x00); + + // Remove all Interrups + mdelay(2); + SL811Write (hci, SL11H_INTSTATREG, 0xff); + } + + if (hp->tl) + kfree (hp->tl); + + sl811_release_regions(hp); + + if (hp->irq >= 0) { + free_irq (hp->irq, hci); + hp->irq = -1; + } + + usb_deregister_bus (hci->bus); + usb_free_bus (hci->bus); + + list_del (&hci->hci_hcd_list); + INIT_LIST_HEAD (&hci->hci_hcd_list); + + kfree (hci); +} + +/***************************************************************** + * + * Function Name: hc_found_hci + * + * This function request IO memory regions, request IRQ, and + * allocate all other resources. + * + * Input: addr = first IO address + * addr2 = second IO address + * irq = interrupt number + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __devinit hc_found_hci (int irq, int iobase1, int iobase2) +{ + hci_t *hci; + hcipriv_t *hp; + + DBGFUNC ("Enter hc_found_hci\n"); + hci = hc_alloc_hci (); + if (!hci) { + return -ENOMEM; + } + + init_irq (); + hp = &hci->hp; + + if (sl811_request_regions (hp, iobase1, iobase2)) { + hc_release_hci (hci); + return -EBUSY; + } + + if (!regTest (hci)) { + DBGERR (KERN_ERR "regTest: Controller fault!\n"); + hc_release_hci (hci); + return -ENXIO; /* No such device or address */ + } + + if (hc_alloc_trans_buffer (hci)) { + hc_release_hci (hci); + return -ENOMEM; + } + + usb_register_bus (hci->bus); + + if (request_irq (irq, hc_interrupt, SA_SHIRQ, MODNAME, hci) != 0) { + DBGERR ("request interrupt %d failed", irq); + hc_release_hci (hci); + return -EBUSY; + } + hp->irq = irq; + + printk (KERN_INFO __FILE__ ": USB SL811 at %x,%x, IRQ %d\n", + hp->hcport, hp->hcport2, irq); + + #ifdef SL811_DEBUG_VERBOSE + { + __u8 u = SL811Read (hci, SL11H_HWREVREG); + + DBGVERBOSE ("SL811 HW: %02Xh ", u); + switch (u & 0xF0) { + case 0x00: DBGVERBOSE ("SL11H\n"); break; + case 0x10: DBGVERBOSE ("SL811HS rev1.2\n"); break; + case 0x20: DBGVERBOSE ("SL811HS rev1.5\n"); break; + default: DBGVERBOSE ("unknown!\n"); + } + } + #endif // SL811_DEBUG_VERBOSE + + if (hc_reset (hci)) { + hc_release_hci (hci); + return -EBUSY; + } + + if (hc_start (hci) < 0) { + DBGERR ("can't start usb-%x", hp->hcport); + hc_release_hci (hci); + return -EBUSY; + } + + return 0; +} + +/***************************************************************** + * + * Function Name: hci_hcd_init + * + * This is an init function, and it is the first function being called + * + * Input: none + * + * Return: 0 = success or error condition + * + *****************************************************************/ +static int __init hci_hcd_init (void) +{ + int ret; +#ifndef __arm__ + int io_offset = 0; +#endif // !__arm__ + + DBGFUNC ("Enter hci_hcd_init\n"); + DBGVERBOSE ("SL811 VERBOSE enabled\n"); + +#ifdef __arm__ + ret = hc_found_hci (irq, base_addr, data_reg_addr); +#else // __arm__ + + // registering "another instance" + for (io_offset = 0; io_offset < MAX_CONTROLERS * 2; io_offset += 2) { + + ret = hc_found_hci (irq, io_base + io_offset, 0); + if (ret) + return (ret); + + } /* endfor */ +#endif // __arm__ + + return ret; +} + +/***************************************************************** + * + * Function Name: hci_hcd_cleanup + * + * This is a cleanup function, and it is called when module is + * unloaded. + * + * Input: none + * + * Return: none + * + *****************************************************************/ +static void __exit hci_hcd_cleanup (void) +{ + struct list_head *hci_l; + hci_t *hci; + + DBGFUNC ("Enter hci_hcd_cleanup\n"); + for (hci_l = hci_hcd_list.next; hci_l != &hci_hcd_list;) { + hci = list_entry (hci_l, hci_t, hci_hcd_list); + hci_l = hci_l->next; + hc_release_hci (hci); + } +} + +module_init (hci_hcd_init); +module_exit (hci_hcd_cleanup); + +MODULE_AUTHOR ("Pei Liu , Henry Nestler "); +MODULE_DESCRIPTION ("USB SL811HS Host Controller Driver"); diff -Nru a/drivers/usb/host/hc_sl811.h b/drivers/usb/host/hc_sl811.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/hc_sl811.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,386 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC + * + * + */ + +#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber) + +/* + * Maximum number of root hub ports + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* control and status registers */ +#define HcRevision 0x00 +#define HcControl 0x01 +#define HcCommandStatus 0x02 +#define HcInterruptStatus 0x03 +#define HcInterruptEnable 0x04 +#define HcInterruptDisable 0x05 +#define HcFmInterval 0x0D +#define HcFmRemaining 0x0E +#define HcFmNumber 0x0F +#define HcLSThreshold 0x11 +#define HcRhDescriptorA 0x12 +#define HcRhDescriptorB 0x13 +#define HcRhStatus 0x14 +#define HcRhPortStatus 0x15 + +#define HcHardwareConfiguration 0x20 +#define HcDMAConfiguration 0x21 +#define HcTransferCounter 0x22 +#define HcuPInterrupt 0x24 +#define HcuPInterruptEnable 0x25 +#define HcChipID 0x27 +#define HcScratch 0x28 +#define HcSoftwareReset 0x29 +#define HcITLBufferLength 0x2A +#define HcATLBufferLength 0x2B +#define HcBufferStatus 0x2C +#define HcReadBackITL0Length 0x2D +#define HcReadBackITL1Length 0x2E +#define HcITLBufferPort 0x40 +#define HcATLBufferPort 0x41 + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_HCFS (3 << 6) /* BUS state mask */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#define OHCI_USB_RESET (0 << 6) +#define OHCI_USB_RESUME (1 << 6) +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_SO (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ + +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_ATD (1 << 7) /* scheduling overrun */ + +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + +/* + * HcHardwareConfiguration + */ +#define InterruptPinEnable (1 << 0) +#define InterruptPinTrigger (1 << 1) +#define InterruptOutputPolarity (1 << 2) +#define DataBusWidth16 (1 << 3) +#define DREQOutputPolarity (1 << 5) +#define DACKInputPolarity (1 << 6) +#define EOTInputPolarity (1 << 7) +#define DACKMode (1 << 8) +#define AnalogOCEnable (1 << 10) +#define SuspendClkNotStop (1 << 11) +#define DownstreamPort15KRSel (1 << 12) + +/* + * HcDMAConfiguration + */ +#define DMAReadWriteSelect (1 << 0) +#define ITL_ATL_DataSelect (1 << 1) +#define DMACounterSelect (1 << 2) +#define DMAEnable (1 << 4) +#define BurstLen_1 0 +#define BurstLen_4 (1 << 5) +#define BurstLen_8 (2 << 5) + +/* + * HcuPInterrupt + */ +#define SOFITLInt (1 << 0) +#define ATLInt (1 << 1) +#define AllEOTInterrupt (1 << 2) +#define OPR_Reg (1 << 4) +#define HCSuspended (1 << 5) +#define ClkReady (1 << 6) + +/* + * HcBufferStatus + */ +#define ITL0BufferFull (1 << 0) +#define ITL1BufferFull (1 << 1) +#define ATLBufferFull (1 << 2) +#define ITL0BufferDone (1 << 3) +#define ITL1BufferDone (1 << 4) +#define ATLBufferDone (1 << 5) + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status */ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define URB_DEL 1 + +#define PORT_STAT_DEFAULT 0x0100 +#define PORT_CONNECT_STAT 0x1 +#define PORT_ENABLE_STAT 0x2 +#define PORT_SUSPEND_STAT 0x4 +#define PORT_OVER_CURRENT_STAT 0x8 +#define PORT_RESET_STAT 0x10 +#define PORT_POWER_STAT 0x100 +#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200 + +#define PORT_CHANGE_DEFAULT 0x0 +#define PORT_CONNECT_CHANGE 0x1 +#define PORT_ENABLE_CHANGE 0x2 +#define PORT_SUSPEND_CHANGE 0x4 +#define PORT_OVER_CURRENT_CHANGE 0x8 +#define PORT_RESET_CHANGE 0x10 + +/* Port Status Request info */ + +typedef struct portstat { + __u16 portChange; + __u16 portStatus; +} portstat_t; + +typedef struct hcipriv { + int irq; + int disabled; /* e.g. got a UE, we're hung */ + atomic_t resume_count; /* defending against multiple resumes */ + struct ohci_regs *regs; /* OHCI controller's memory */ + int hcport; /* I/O base address */ + int hcport2; /* I/O data reg addr */ + + struct portstat *RHportStatus; /* root hub port status */ + + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + + int frame; + + __u8 *tl; + int xferPktLen; + int atl_len; + int atl_buffer_len; + int itl0_len; + int itl1_len; + int itl_buffer_len; + int itl_index; + int tl_last; + int units_left; + +} hcipriv_t; +struct hci; + +#define cClt 0 // Control +#define cISO 1 // ISO +#define cBULK 2 // BULK +#define cInt 3 // Interrupt +#define ISO_BIT 0x10 + +/*------------------------------------------------------------------------- + * EP0 use for configuration and Vendor Specific command interface + *------------------------------------------------------------------------*/ +#define cMemStart 0x10 +#define EP0Buf 0x40 /* SL11H/SL811H memory start at 0x40 */ +#define EP0Len 0x40 /* Length of config buffer EP0Buf */ +#define EP1Buf 0x60 +#define EP1Len 0x40 + +/*------------------------------------------------------------------------- + * SL11H/SL811H memory from 80h-ffh use as ping-pong buffer. + *------------------------------------------------------------------------*/ +#define uBufA 0x80 /* buffer A address for DATA0 */ +#define uBufB 0xc0 /* buffer B address for DATA1 */ +#define uXferLen 0x40 /* xfer length */ +#define sMemSize 0xc0 /* Total SL11 memory size */ +#define cMemEnd 256 + +/*------------------------------------------------------------------------- + * SL811H Register Control memory map + * --Note: + * --SL11H only has one control register set from 0x00-0x04 + * --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c + *------------------------------------------------------------------------*/ + +#define EP0Control 0x00 +#define EP0Address 0x01 +#define EP0XferLen 0x02 +#define EP0Status 0x03 +#define EP0Counter 0x04 + +#define EP1Control 0x08 +#define EP1Address 0x09 +#define EP1XferLen 0x0a +#define EP1Status 0x0b +#define EP1Counter 0x0c + +#define CtrlReg 0x05 +#define IntEna 0x06 + // 0x07 is reserved +#define IntStatus 0x0d +#define cDATASet 0x0e +#define cSOFcnt 0x0f +#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ +#define HostMask 0x47 /* Host request command for IntStatus */ +#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ + +/*------------------------------------------------------------------------- + * Standard Chapter 9 definition + *------------------------------------------------------------------------- + */ +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIG 0x08 +#define SET_CONFIG 0x09 +#define GET_INTERFACE 0x0a +#define SET_INTERFACE 0x0b +#define SYNCH_FRAME 0x0c + +#define DEVICE 0x01 +#define CONFIGURATION 0x02 +#define STRING 0x03 +#define INTERFACE 0x04 +#define ENDPOINT 0x05 + +/*------------------------------------------------------------------------- + * SL11H/SL811H definition + *------------------------------------------------------------------------- + */ +#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) +#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) +#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) +#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) +#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) +#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) + +#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition +#define PID_SOF 0xA5 +#define PID_IN 0x69 +#define PID_OUT 0xe1 + +#define MAX_RETRY 0xffff +#define TIMEOUT 5 /* 2 mseconds */ + +#define SL11H_HOSTCTLREG 0 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +#define SL11H_PIDEPREG 3 /* write */ +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ +#define SL11H_CTLREG1 5 +#define SL11H_INTENBLREG 6 + +// You should not use these registers + #define SL11H_HOSTCTLREG_B 8 +// #define SL11H_BUFADDRREG_B 9 + #define SL11H_BUFLNTHREG_B 0x0A +// #define SL11H_PKTSTATREG_B 0x0B /* read */ + #define SL11H_PIDEPREG_B 0x0B /* write */ +// #define SL11H_XFERCNTREG_B 0x0C /* read */ + #define SL11H_DEVADDRREG_B 0x0C /* write */ + +#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ +#define SL11H_HWREVREG 0x0E /* read */ +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ +#define SL11H_CTLREG2 0x0F /* write */ +#define SL11H_DATA_START 0x10 + +/* Host control register bits (addr 0) */ +#define SL11H_HCTLMASK_ARM 1 +#define SL11H_HCTLMASK_ENBLEP 2 +#define SL11H_HCTLMASK_WRITE 4 +#define SL11H_HCTLMASK_ISOCH 0x10 +#define SL11H_HCTLMASK_AFTERSOF 0x20 +#define SL11H_HCTLMASK_SEQ 0x40 +#define SL11H_HCTLMASK_PREAMBLE 0x80 + +/* Packet status register bits (addr 3) */ +#define SL11H_STATMASK_ACK 1 +#define SL11H_STATMASK_ERROR 2 +#define SL11H_STATMASK_TMOUT 4 +#define SL11H_STATMASK_SEQ 8 +#define SL11H_STATMASK_SETUP 0x10 +#define SL11H_STATMASK_OVF 0x20 +#define SL11H_STATMASK_NAK 0x40 +#define SL11H_STATMASK_STALL 0x80 + +/* Control register 1 bits (addr 5) */ +#define SL11H_CTL1MASK_DSBLSOF 1 +#define SL11H_CTL1MASK_NOTXEOF2 4 +#define SL11H_CTL1MASK_DSTATE 0x18 +#define SL11H_CTL1MASK_NSPD 0x20 +#define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_CTL1MASK_CLK12 0x80 + +#define SL11H_CTL1VAL_RESET 8 + +/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ +#define SL11H_INTMASK_XFERDONE 1 +#define SL11H_INTMASK_SOFINTR 0x10 +#define SL11H_INTMASK_INSRMV 0x20 +#define SL11H_INTMASK_USBRESET 0x40 +#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ + +/* HW rev and SOF lo register bits (addr 0xE) */ +#define SL11H_HWRMASK_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0xF) */ +#define SL11H_CTL2MASK_SOFHI 0x3F +#define SL11H_CTL2MASK_DSWAP 0x40 +#define SL11H_CTL2MASK_HOSTMODE 0xae + diff -Nru a/drivers/usb/host/hc_sl811_rh.c b/drivers/usb/host/hc_sl811_rh.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/host/hc_sl811_rh.c Fri Oct 24 17:01:14 2003 @@ -0,0 +1,536 @@ + +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------* + * SL811HS virtual root hub + * + * based on usb-ohci.c by R. Weissgaerber et al. + *-------------------------------------------------------------------------* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#undef DEBUG +#endif +static __u32 getPortStatusAndChange (hci_t * hci); +static void setPortStatus (hci_t * hci, __u16 bitPos); +static void setPortChange (hci_t * hci, __u16 bitPos); +static void clrPortStatus (hci_t * hci, __u16 bitPos); +static void clrPortChange (hci_t * hci, __u16 bitPos); +static int USBReset (hci_t * hci); +static int cc_to_error (int cc); + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* [Hub class-specific descriptor is constructed dynamically] */ +/* copied from "dynamic source". (hne) */ +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x01, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x50, /* __u8 bPwrOn2pwrGood; 100ms for port reset */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0xfc, /* ??? HNE */ /* __u8 DeviceRemovable; *** 7 Ports max *** */ /* which port is attachable (HNE) */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + + +/*************************************************************************** + * Function Name : rh_send_irq + * + * This function examine the port change in the virtual root hub. + * + * Note: This function assumes only one port exist in the root hub. + * + * Input: hci = data structure for the host controller + * rh_data = The pointer to port change data + * rh_len = length of the data in bytes + * + * Return: length of data + **************************************************************************/ +static int rh_send_irq (hci_t * hci, void *rh_data, int rh_len) +{ + int num_ports; + int i; + int ret; + int len; + __u8 data[8]; + + DBGFUNC ("enter rh_send_irq: \n"); + + /* Assuming the root hub has one port. This value need to change if + * there are more than one port for the root hub + */ + + num_ports = 1; + + /* The root hub status is not implemented, it basically has two fields: + * -- Local Power Status + * -- Over Current Indicator + * -- Local Power Change + * -- Over Current Indicator + * + * Right now, It is assume the power is good and no changes + */ + + *(__u8 *) data = 0; + + ret = *(__u8 *) data; + + /* Has the port status change within the root hub: It checks for + * -- Port Connect Status change + * -- Port Enable Change + */ + + for (i = 0; i < num_ports; i++) { + *(__u8 *) (data + (i + 1) / 8) |= + (((getPortStatusAndChange (hci) >> 16) & (PORT_CONNECT_STAT | PORT_ENABLE_STAT)) ? 1 : 0) << ((i + 1) % 8); + ret += *(__u8 *) (data + (i + 1) / 8); + + /* After the port change is read, it should be reset so the next time + * is it doesn't trigger a change again */ + + } + len = i / 8 + 1; + + if (ret > 0) { + memcpy (rh_data, data, min (len, (int)min (rh_len, (int)sizeof (data)))); + return len; + } + return 0; +} + +/*************************************************************************** + * Function Name : rh_int_timer_do + * + * This function is called when the timer expires. It gets the the port + * change data and pass along to the upper protocol. + * + * Note: The virtual root hub interrupt pipe are polled by the timer + * every "interval" ms + * + * Input: ptr = ptr to the urb + * + * Return: none + **************************************************************************/ +static void rh_int_timer_do (unsigned long ptr) +{ + int len; + struct urb *urb = (struct urb *) ptr; + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_int_timer_do\n"); + + if (hci->rh.send) { + len = rh_send_irq (hci, urb->transfer_buffer, + urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; + if (urb_debug == 2) + urb_print (urb, "RET-t(rh)", + usb_pipeout (urb->pipe)); + + if (urb->complete) { + urb->complete (urb); + } + } + } + + /* re-activate the timer */ + rh_init_int_timer (urb); +} + +/*************************************************************************** + * Function Name : rh_init_int_timer + * + * This function creates a timer that act as interrupt pipe in the + * virtual hub. + * + * Note: The virtual root hub's interrupt pipe are polled by the timer + * every "interval" ms + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_init_int_timer (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + hci->rh.interval = urb->interval; + + init_timer (&hci->rh.rh_int_timer); + hci->rh.rh_int_timer.function = rh_int_timer_do; + hci->rh.rh_int_timer.data = (unsigned long) urb; + hci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer (&hci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* helper macro */ +#define OK(x) len = (x); break + +/*************************************************************************** + * Function Name : rh_submit_urb + * + * This function handles all USB request to the the virtual root hub + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_submit_urb (struct urb * urb) +{ + struct usb_device *usb_dev = urb->dev; + hci_t *hci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = TD_CC_NOERROR; + __u32 datab[4]; + __u8 *data_buf = (__u8 *) datab; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + DBGFUNC ("enter rh_submit_urb\n"); + if (usb_pipeint (pipe)) { + hci->rh.urb = urb; + hci->rh.send = 1; + hci->rh.interval = urb->interval; + rh_init_int_timer (urb); + urb->status = cc_to_error (TD_CC_NOERROR); + + return 0; + } + + bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); + wValue = le16_to_cpu (cmd->wValue); + wIndex = le16_to_cpu (cmd->wIndex); + wLength = le16_to_cpu (cmd->wLength); + + DBG ("rh_submit_urb, req = %d(%x) len=%d", + bmRType_bReq, bmRType_bReq, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = cpu_to_le16 (1); + OK (2); + + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16 (0); + OK (2); + + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (0); + OK (4); + + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = + cpu_to_le32 (getPortStatusAndChange (hci)); + OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK (0); + + case (RH_C_HUB_OVER_CURRENT): + /* Over Current Not Implemented */ + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + clrPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_PORT_SUSPEND): + clrPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_POWER): + clrPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_C_PORT_CONNECTION): + clrPortChange (hci, PORT_CONNECT_STAT); + OK (0); + + case (RH_C_PORT_ENABLE): + clrPortChange (hci, PORT_ENABLE_STAT); + OK (0); + + case (RH_C_PORT_SUSPEND): + clrPortChange (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_C_PORT_OVER_CURRENT): + clrPortChange (hci, PORT_OVER_CURRENT_STAT); + OK (0); + + case (RH_C_PORT_RESET): + clrPortChange (hci, PORT_RESET_STAT); + OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + setPortStatus (hci, PORT_SUSPEND_STAT); + OK (0); + + case (RH_PORT_RESET): + setPortStatus (hci, PORT_RESET_STAT); + // USBReset(hci); + clrPortChange (hci, + PORT_CONNECT_CHANGE | PORT_ENABLE_CHANGE + | PORT_SUSPEND_CHANGE | + PORT_OVER_CURRENT_CHANGE); + setPortChange (hci, PORT_RESET_CHANGE); + clrPortStatus (hci, PORT_RESET_STAT); + setPortStatus (hci, PORT_ENABLE_STAT); + + OK (0); + + case (RH_PORT_POWER): + setPortStatus (hci, PORT_POWER_STAT); + OK (0); + + case (RH_PORT_ENABLE): + setPortStatus (hci, PORT_ENABLE_STAT); + OK (0); + } + break; + + case RH_SET_ADDRESS: + hci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + DBGVERBOSE ("rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x\n", wValue); + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; + OK (len); + + case (0x02): /* configuration descriptor */ + len = min (leni, min ((__u16)sizeof (root_hub_config_des), wLength)); + data_buf = root_hub_config_des; + OK (len); + + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, (int) (long) 0, + "SL811HS", data, wLength); + if (len > 0) { + data_buf = data; + OK (min (leni, len)); + } + + default: + status = SL11H_STATMASK_STALL; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + len = min_t(unsigned int, leni, + min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); + memcpy (data_buf, root_hub_hub_des, len); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data_buf = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + + default: + DBGERR ("unsupported root hub command"); + status = SL11H_STATMASK_STALL; + } + + len = min (len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + urb->actual_length = len; + urb->status = cc_to_error (status); + + urb->hcpriv = NULL; + urb->dev = NULL; + if (urb->complete) { + urb->complete (urb); + } + + return 0; +} + +/*************************************************************************** + * Function Name : rh_unlink_urb + * + * This function unlinks the URB + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_unlink_urb (struct urb * urb) +{ + hci_t *hci = urb->dev->bus->hcpriv; + + DBGFUNC ("enter rh_unlink_urb\n"); + if (hci->rh.urb == urb) { + hci->rh.send = 0; + del_timer (&hci->rh.rh_int_timer); + hci->rh.urb = NULL; + + urb->hcpriv = NULL; + /* --- + usb_put_dev (urb->dev); + + Can not found this function, copy from old source and other source. + usbnet.c us this to. Its a macro to usb_dec_dev_use. (hne) + --- */ + usb_dec_dev_use(urb->dev); + urb->dev = NULL; + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNRESET; + if (urb->complete) { + urb->complete (urb); + } + } else + urb->status = -ENOENT; + } + return 0; +} + +/*************************************************************************** + * Function Name : rh_connect_rh + * + * This function connect the virtual root hub to the USB stack + * + * Input: urb = USB request block + * + * Return: 0 + **************************************************************************/ +static int rh_connect_rh (hci_t * hci) +{ + struct usb_device *usb_dev; + + hci->rh.devnum = 0; + usb_dev = usb_alloc_dev (NULL, hci->bus); + if (!usb_dev) + return -ENOMEM; + + hci->bus->root_hub = usb_dev; + usb_connect (usb_dev); + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -ENODEV; + } + + return 0; +} diff -Nru a/drivers/usb/host/sl811.c b/drivers/usb/host/sl811.c --- a/drivers/usb/host/sl811.c Fri Oct 24 17:01:14 2003 +++ b/drivers/usb/host/sl811.c Fri Oct 24 17:01:14 2003 @@ -3,18 +3,43 @@ * * Copyright (c) 2003/06, Courage Co., Ltd. * - * Based on: + * Based on: * 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap, * Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, * Adam Richter, Gregory P. Smith; - 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 2.Original SL811 driver (hc_sl811.o) by Pei Liu + * 3.Rewrited as sl811.o by Yin Aihua * - * It's now support isosynchronous mode and more effective than hc_sl811.o + * It's now support isochornous mode and more effective than hc_sl811.o + * Support x86 architecture now. + * + * 19.09.2003 (05.06.2003) HNE + * sl811_alloc_hc: Set "bus->bus_name" at init. + * sl811_reg_test (hc_reset,regTest): + * Stop output at first failed pattern. + * Down-Grade for Kernel 2.4.20 and from 2.4.22 + * Splitt hardware depens into file sl811-x86.h and sl811-arm.h. + * + * 22.09.2003 HNE + * sl811_found_hc: First patterntest, than interrupt enable. + * Do nothing, if patterntest failed. Release io, if failed. + * Stop Interrupts first, than remove handle. (Old blocked Shred IRQ) + * Alternate IO-Base for second Controller (CF/USB1). + * + * 24.09.2003 HNE + * Remove all arm specific source (moved into include/asm/sl811-hw.h). + * + * 03.10.2003 HNE + * Low level only for port io into hardware-include. + * * To do: * 1.Modify the timeout part, it's some messy * 2.Use usb-a and usb-b set in Ping-Pong mode - + * o Floppy do not work. + * o driver crash, if io region can't register + * o Only as module tested! Compiled in Version not tested! + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -41,13 +66,10 @@ #include "../hub.h" #include "sl811.h" -#define DRIVER_VERSION "v0.28" -#define DRIVER_AUTHOR "Yin Aihua " -#define DRIVER_DESC "Sl811 USB Host Controller Driver" - -static int sl811_addr_io = 0xf100000e; -static int sl811_data_io = 0xf100000f; -static int sl811_irq = 44; +#define DRIVER_VERSION "v0.30" +#define MODNAME "SL811" +#define DRIVER_AUTHOR "Yin Aihua , Henry Nestler " +#define DRIVER_DESC "Sl811 USB Host Controller Alternate Driver" static LIST_HEAD(sl811_hcd_list); @@ -57,70 +79,53 @@ * 2, error maybe occur in normal * 3, useful and detail debug information * 4, function level enter and level inforamtion - * 5, endless information will output because of timer function or interrupt + * 5, endless information will output because of timer function or interrupt */ static int debug = 0; - -MODULE_PARM(sl811_addr_io,"i"); -MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e"); -MODULE_PARM(sl811_data_io,"i"); -MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f"); -MODULE_PARM(sl811_irq,"i"); -MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)"); MODULE_PARM(debug,"i"); MODULE_PARM_DESC(debug,"debug level"); +#include /* Include hardware and board depens */ + static void sl811_rh_int_timer_do(unsigned long ptr); -static void sl811_transfer_done(struct sl811_hc *hc, int sof); +static void sl811_transfer_done(struct sl811_hc *hc, int sof); /* * Read a byte of data from the SL811H/SL11H */ -static __u8 sl811_read(struct sl811_hc *hc, __u8 offset) +static __u8 inline sl811_read(struct sl811_hc *hc, __u8 offset) { - __u8 data; - - writeb(offset, hc->addr_io); - wmb(); - data = readb(hc->data_io); - rmb(); - - return data; + sl811_write_index (hc, offset); + return (sl811_read_data (hc)); } /* * Write a byte of data to the SL811H/SL11H */ -static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) +static void inline sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data) { - writeb(offset, hc->addr_io); - writeb(data, hc->data_io); - wmb(); + sl811_write_index_data (hc, offset, data); } /* * Read consecutive bytes of data from the SL811H/SL11H buffer */ -static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +static void inline sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { - writeb(offset, hc->addr_io); - wmb(); + sl811_write_index (hc, offset); while (size--) { - *buf++ = readb(hc->data_io); - rmb(); + *buf++ = sl811_read_data(hc); } } /* * Write consecutive bytes of data to the SL811H/SL11H buffer */ -static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) +static void inline sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size) { - writeb(offset, hc->addr_io); - wmb(); + sl811_write_index (hc, offset); while (size--) { - writeb(*buf, hc->data_io); - wmb(); + sl811_write_data (hc, *buf); buf++; } } @@ -147,6 +152,10 @@ if (data != i) { PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i); result = -1; + + /* If no Debug, show only first failed Address */ + if (!debug) + break; } } @@ -160,6 +169,7 @@ /* * Display all SL811HS register values */ +#if 0 /* unused (hne) */ static void sl811_reg_show(struct sl811_hc *hc) { int i; @@ -167,6 +177,7 @@ for (i = 0; i < 256; i++) PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i)); } +#endif /* * This function enables SL811HS interrupts @@ -845,7 +856,7 @@ if (urbp->cur_td == NULL) urbp->cur_td = urbp->first_td = td; } - + urbp->last_td = td; PDEBUG(4, "leave success"); @@ -2397,7 +2408,7 @@ struct sl811_td *next_td; #ifdef SL811_DEBUG static struct sl811_td *repeat_td = NULL; - static repeat_cnt = 1; + static int repeat_cnt = 1; #endif if (++hc->frame_number > 1024) hc->frame_number = 0; @@ -2464,7 +2475,7 @@ mdelay(20); - // Disable hardware SOF generation. + // Disable hardware SOF generation, clear all irq status. sl811_write(hc, SL811_CTRL1, 0); mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); @@ -2532,7 +2543,7 @@ status = sl811_read(hc, SL811_INTRSTS); if (status == 0) - return ; + return ; /* Not me */ sl811_write(hc, SL811_INTRSTS, 0xff); @@ -2603,6 +2614,7 @@ } hc->bus = bus; + bus->bus_name = MODNAME; bus->hcpriv = hc; return hc; @@ -2619,14 +2631,23 @@ if (hc->bus->root_hub) usb_disconnect(&hc->bus->root_hub); - if (hc->addr_io) - release_region(hc->addr_io, 1); - - if (hc->data_io) - release_region(hc->data_io, 1); - + // Stop interrupt handle if (hc->irq) free_irq(hc->irq, hc); + hc->irq = 0; + + /* Stop interrupt for sharing, but only, if PatternTest ok */ + if (hc->addr_io) { + /* Disable Interrupts */ + sl811_write(hc, SL811_INTR, 0); + + /* Remove all Interrupt events */ + mdelay(2); + sl811_write(hc, SL811_INTRSTS, 0xff); + } + + /* free io regions */ + sl811_release_regions(hc); usb_deregister_bus(hc->bus); usb_free_bus(hc->bus); @@ -2638,16 +2659,6 @@ } /* - * This function is board specific. It sets up the interrupt to - * be an edge trigger and trigger on the rising edge - */ -static void sl811_init_irq(void) -{ - GPDR &= ~(1<<23); - set_GPIO_IRQ_edge(1<<23, GPIO_RISING_EDGE); -} - -/* * This function request IO memory regions, request IRQ, and * allocate all other resources. * @@ -2667,40 +2678,47 @@ if (!hc) return -ENOMEM; - sl811_init_irq(); - - if (!request_region(addr_io, 1, "SL811 USB HOST")) { - PDEBUG(1, "request address %d failed", addr_io); + if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { + PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); sl811_release_hc(hc); return -EBUSY; } - hc->addr_io = addr_io; - if (!request_region(data_io, 1, "SL811 USB HOST")) { - PDEBUG(1, "request address %d failed", data_io); - sl811_release_hc (hc); - return -EBUSY; + if (sl811_reg_test(hc)) { + PDEBUG(1, "SL811 register test failed!"); + sl811_release_hc(hc); + return -ENODEV; + } + +#ifdef SL811_DEBUG_VERBOSE + { + __u8 u = SL811Read (hci, SL11H_HWREVREG); + + // Show the hardware revision of chip + PDEBUG(1, "SL811 HW: %02Xh", u); + switch (u & 0xF0) { + case 0x00: PDEBUG(1, "SL11H"); break; + case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; + case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; + default: PDEBUG(1, "Revision unknown!"); + } } - hc->data_io = data_io; +#endif // SL811_DEBUG_VERBOSE + + sl811_init_irq(); usb_register_bus(hc->bus); - if (request_irq(irq, sl811_interrupt, 0, "SL811", hc)) { + if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) { PDEBUG(1, "request interrupt %d failed", irq); sl811_release_hc(hc); return -EBUSY; } hc->irq = irq; - printk(KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n", + printk(KERN_INFO __FILE__ ": USB SL811 at %x,%x, IRQ %d\n", addr_io, data_io, irq); - if (sl811_reg_test(hc)) { - PDEBUG(1, "SL811 register test failed!"); - sl811_release_hc(hc); - return -ENODEV; - } - sl811_hc_reset(hc); sl811_connect_rh(hc); @@ -2714,13 +2732,21 @@ */ static int __init sl811_hcd_init(void) { - int ret; - - PDEBUG(5, "etner"); + int ret = -ENODEV; + int count; + + PDEBUG(5, "enter"); info(DRIVER_VERSION " : " DRIVER_DESC); - - ret = sl811_found_hc(sl811_addr_io, sl811_data_io, sl811_irq); + + // registering some instance + for (count = 0; count < MAX_CONTROLERS; count++) { + if (io[count]) { + ret = sl811_found_hc(io[count], io[count]+OFFSET_DATA_REG, irq[count]); + if (ret) + return (ret); + } + } return ret; } diff -Nru a/include/asm-arm/hc_sl811-hw.h b/include/asm-arm/hc_sl811-hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/hc_sl811-hw.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,139 @@ +/* +File: include/asm-arm/hc_sl811-hw.h +*/ + +/* The base_addr, data_reg_addr, and irq number are board specific. + * The current values are design to run on the Accelent SA1110 IDP + * NOTE: values need to modify for different development boards + */ + +#define SIZEOF_IO_REGION 256 /* Size for request/release region */ +static int base_addr = 0xd3800000; +static int data_reg_addr = 0xd3810000; +static int irq = 34; /* Also change SL811_SET_GPDR! */ +static int gprd = 13; /* also change irq !!! */ + +MODULE_PARM (base_addr, "i"); +MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); +MODULE_PARM (data_reg_addr, "i"); +MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); +MODULE_PARM (irq, "i"); +MODULE_PARM_DESC (irq, "IRQ 34 (default)"); +MODULE_PARM(gprd,"i"); +MODULE_PARM_DESC(gprd,"sl811 GPRD port 13(default)"); + +/* + * Low level: Read from Data port [arm] + */ +static __u8 inline sl811_read_data (hcipriv_t *hp) +{ + __u8 data; + data = readb (hp->hcport2); + rmb (); + return (data); +} + +/* + * Low level: Write to index register [arm] + */ +static void inline sl811_write_index (hcipriv_t *hp, __u8 index) +{ + writeb (offset, hp->hcport); + wmb (); +} + +/* + * Low level: Write to Data port [arm] + */ +static void inline sl811_write_data (hcipriv_t *hp, __u8 data) +{ + writeb (data, hp->hcport2); +} + +/* + * Low level: Write to index register and data port [arm] + */ +static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data) +{ + writeb (offset, hp->hcport); + writeb (data, hp->hcport2); + wmb (); +} + +/***************************************************************** + * + * Function Name: init_irq [arm] + * + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + * + * Input: none + * + * Return value : none + * + *****************************************************************/ +static void inline init_irq (void) +{ + GPDR &= ~(1 << gprd); + set_GPIO_IRQ_edge (1 << gprd, GPIO_RISING_EDGE); +} + +/***************************************************************** + * + * Function Name: release_regions [arm] + * + * This function is board specific. It frees all io address. + * + * Input: hcipriv_t * + * + * Return value : none + * + *****************************************************************/ +static void inline release_regions (hcipriv_t *hp) +{ + if (hp->hcport > 0) { + release_region (hp->hcport, SIZEOF_IO_REGION); + hp->hcport = 0; + } + + if (hp->hcport2 > 0) { + release_region (hp->hcport2, SIZEOF_IO_REGION); + hp->hcport2 = 0; + } +} + +/***************************************************************** + * + * Function Name: request_regions [arm] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: hcipriv_t * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline request_regions (hcipriv_t *hp, int addr1, int addr2) +{ + if (!request_region (addr1, SIZEOF_IO_REGION, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr1); + return -EBUSY; + } + hp->hcport = addr1; + if (!hp->hcport) { + DBGERR ("Error mapping SL811 Memory 0x%x", addr1); + } + + if (!request_region (addr2, SIZEOF_IO_REGION, "SL811 USB HOST")) { + DBGERR ("request address %d failed", addr2); + return -EBUSY; + } + hp->hcport2 = addr2; + if (!hp->hcport2) { + DBGERR ("Error mapping SL811 Memory 0x%x", addr2); + } + + return 0; +} + diff -Nru a/include/asm-arm/sl811-hw.h b/include/asm-arm/sl811-hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-arm/sl811-hw.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,148 @@ +/* +File: include/asm-arm/sl811-hw.h + +19.09.2003 hne@ist1.de +Use Kernel 2.4.20 and this source from 2.4.22 +Splitt hardware depens into file sl811-x86.h and sl811-arm.h. +Functions as inline. + +23.09.2003 hne +Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h. +GPRD as parameter. + +24.09.2003 hne +Use Offset from ADDR to DATA instand of direct io. + +03.10.2003 hne +Low level only for port io into hardware-include. +*/ + +#ifndef __LINUX_SL811_HW_H +#define __LINUX_SL811_HW_H + +#define MAX_CONTROLERS 1 /* Max number of sl811 controllers */ + /* Always 1 for this architecture! */ + +#define SIZEOF_IO_REGION 1 /* Size for request/release region */ + +#define OFFSET_DATA_REG data_off /* Offset from ADDR_IO to DATA_IO (future) */ + /* Can change by arg */ + +static int io = 0xf100000e; /* Base addr_io */ +static int data_off = 1; /* Offset from addr_io to addr_io */ +static int irq = 44; /* also change gprd !!! */ +static int gprd = 23; /* also change irq !!! */ + +MODULE_PARM(io,"i"); +MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e"); +MODULE_PARM(data_off,"i"); +MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"sl811 irq 44(default)"); +MODULE_PARM(gprd,"i"); +MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)"); + +/* + * Low level: Read from Data port [arm] + */ +static __u8 inline sl811_read_data (struct sl811_hc *hc) +{ + __u8 data; + data = readb(hc->data_io); + rmb(); + return data; +} + +/* + * Low level: Write to index register [arm] + */ +static void inline sl811_write_index (struct sl811_hc *hc, __u8 index) +{ + writeb(index, hc->addr_io); + wmb(); +} + +/* + * Low level: Write to Data port [arm] + */ +static void inline sl811_write_data (struct sl811_hc *hc, __u8 data) +{ + writeb(data, hc->data_io); + wmb(); +} + +/* + * Low level: Write to index register and data port [arm] + */ +static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data) +{ + writeb(index, hc->addr_io); + writeb(data, hc->data_io); + wmb(); +} + + +/* + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + */ +static void inline sl811_init_irq(void) +{ + GPDR &= ~(1<addr_io) + release_region(hc->addr_io, SIZEOF_IO_REGION); + hc->addr_io = 0; + + if (hc->data_io) + release_region(hc->data_io, SIZEOF_IO_REGION); + hc->data_io = 0; +} + +/***************************************************************** + * + * Function Name: request_regions [arm] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: struct sl811_hc * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name) +{ + if (!request_region(addr_io, SIZEOF_IO_REGION, name)) { + PDEBUG(3, "request address %d failed", addr_io); + return -EBUSY; + } + hc->addr_io = addr_io; + + if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) { + PDEBUG(3, "request address %d failed", data_io); + /* release_region(hc->addr_io, SIZEOF_IO_REGION); */ + return -EBUSY; + } + hc->data_io = data_io; + + return 0; +} + +#endif // __LINUX_SL811_HW_H diff -Nru a/include/asm-i386/hc_sl811-hw.h b/include/asm-i386/hc_sl811-hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-i386/hc_sl811-hw.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,139 @@ +/* +File: include/asm-i386/hc_sl811-hw.h + +18.11.2002 hne@ist1.de +Use Kernel 2.4.19 and Prepatch 2.4.20 +Splitt hardware depenc into file hc_sl811-x86.c and hc_sl811-arm.c. + +20.11.2002 HNE +READ/WRITE_(INDEX_)DATA using for fast hardware access. + +02.09.2003 HNE +IO Region size only 2 (old 16) + +18.09.2003 HNE +Handle multi instances. For two controller on one board. +Portcheck in low level source (DEBUG) only. + +03.10.2003 HNE +Low level only for port io into hardware-include. + +*/ + +#ifdef MODULE + +#define MAX_CONTROLERS 2 /* Max number of SL811 controllers per module */ +static int io_base = 0x220; +static int irq = 12; + +MODULE_PARM(io_base,"i"); +MODULE_PARM_DESC(io_base,"sl811 base address 0x220"); +MODULE_PARM(irq,"i"); +MODULE_PARM_DESC(irq,"IRQ 12 (default)"); + +#endif // MODULE + +/* Define general IO Macros for our platform (hne) */ +#define SIZEOF_IO_REGION 2 /* Size for request/release region */ + +// #define sl811_write_index(hp,i) outb ((i), hp->hcport) +// #define sl811_write_data(hp,d) outb ((d), hp->hcport+1) +// #define sl811_write_index_data(hp,i,d) outw ((i)|(((__u16)(d)) << 8), hp->hcport) +// #define sl811_read_data(hp) ((__u8) inb (hp->hcport+1)) + +/* + * Low level: Read from Data port [x86] + */ +static __u8 inline sl811_read_data (hcipriv_t *hp) +{ + return ((__u8) inb (hp->hcport+1)); +} + +/* + * Low level: Write to index register [x86] + */ +static void inline sl811_write_index (hcipriv_t *hp, __u8 index) +{ + outb (index, hp->hcport); +} + +/* + * Low level: Write to Data port [x86] + */ +static void inline sl811_write_data (hcipriv_t *hp, __u8 data) +{ + outb (data, hp->hcport+1); +} + +/* + * Low level: Write to index register and data port [x86] + */ +static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data) +{ + outw (index|(((__u16)data) << 8), hp->hcport); +} + +/***************************************************************** + * + * Function Name: init_irq [x86] + * + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + * + * Input: none + * + * Return value : none + * + *****************************************************************/ +static void inline init_irq (void) +{ + /* nothing */ +} + +/***************************************************************** + * + * Function Name: release_regions [x86] + * + * This function is board specific. It frees all io address. + * + * Input: hcipriv_t * + * + * Return value : none + * + *****************************************************************/ +static void inline sl811_release_regions (hcipriv_t *hp) +{ + DBGFUNC ("Enter release_regions\n"); + if (hp->hcport > 0) { + release_region (hp->hcport, SIZEOF_IO_REGION); + hp->hcport = 0; + } + + /* hcport2 unused for x86 */ +} + +/***************************************************************** + * + * Function Name: request_regions [x86] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: hcipriv_t * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (hcipriv_t *hp, int base1, int base2) +{ + DBGFUNC ("Enter request_regions\n"); + if (!request_region (base1, SIZEOF_IO_REGION, "SL811")) { + DBGERR ("request address 0x%X %d failed\n", base1, SIZEOF_IO_REGION); + return -EBUSY; + } + hp->hcport = base1; + + /* hcport2 unused for x86 */ + return 0; +} + diff -Nru a/include/asm-i386/sl811-hw.h b/include/asm-i386/sl811-hw.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/asm-i386/sl811-hw.h Fri Oct 24 17:01:14 2003 @@ -0,0 +1,133 @@ +/* +File: include/asm-i386/sl811-hw.h + +19.09.2003 hne@ist1.de +Use Kernel 2.4.20 and this source from 2.4.22 +Splitt hardware depens into file sl811-x86.h and sl811-arm.h. +Functions are inline. + +Handle multi instances. For two controller on one board. +"hc->data_io" not used for x86 arch. + +22.09.2003 hne +Alternate IO-Base for second Controller (CF/USB1). + +23.09.2003 hne +Move Hardware depend header sl811-x86.h into include/asm-i386/sl811-hw.h. + +03.10.2003 hne +Low level only for port io into hardware-include. +*/ + +#ifndef __LINUX_SL811_HW_H +#define __LINUX_SL811_HW_H + +#define MAX_CONTROLERS 2 /* Max number of SL811 controllers per module */ + +static int io[MAX_CONTROLERS] = { 0x220, 0x222 }; /* IO ports for controllers */ +static int irq[MAX_CONTROLERS] = { 12, 12 }; /* Interrupt list */ + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CONTROLERS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es), 0x220,0x222 default"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CONTROLERS) "i"); +MODULE_PARM_DESC(irq, "IRQ number(s), 12,12 default"); + +#define OFFSET_DATA_REG 1 /* Offset from ADDR_IO to DATA_IO */ + /* Do not change this! */ +#define SIZEOF_IO_REGION 2 /* Size for request/release region */ + +/* + * Low level: Read from Data port [x86] + */ +static __u8 inline sl811_read_data (struct sl811_hc *hc) +{ + return ((__u8) inb (hc->addr_io+OFFSET_DATA_REG)); +} + +/* + * Low level: Write to index register [x86] + */ +static void inline sl811_write_index (struct sl811_hc *hc, __u8 index) +{ + outb (index, hc->addr_io); +} + +/* + * Low level: Write to Data port [x86] + */ +static void inline sl811_write_data (struct sl811_hc *hc, __u8 data) +{ + outb (data, hc->addr_io+OFFSET_DATA_REG); +} + +/* + * Low level: Write to index register and data port [x86] + */ +static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data) +{ + outw (index|(((__u16)data) << 8), hc->addr_io); +} + +/* + * This function is board specific. It sets up the interrupt to + * be an edge trigger and trigger on the rising edge + */ +static void inline sl811_init_irq(void) +{ + /* nothing for x86 */ +} + + +/***************************************************************** + * + * Function Name: release_regions [x86] + * + * This function is board specific. It release all io address + * from memory (if can). + * + * Input: struct sl811_hc * * + * + * Return value : 0 = OK + * + *****************************************************************/ +static void inline sl811_release_regions(struct sl811_hc *hc) +{ + if (hc->addr_io) + release_region(hc->addr_io, SIZEOF_IO_REGION); + hc->addr_io = 0; + + /* data_io unused for x86 */ + /* ... + if (hc->data_io) + release_region(hc->data_io, SIZEOF_IO_REGION); + hc->data_io = 0; + ... */ +} + +/***************************************************************** + * + * Function Name: request_regions [x86] + * + * This function is board specific. It request all io address and + * maps into memory (if can). + * + * Input: struct sl811_hc * + * + * Return value : 0 = OK + * + *****************************************************************/ +static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name) +{ + if (!request_region(addr_io, SIZEOF_IO_REGION, name)) { + PDEBUG(3, "request address %d failed", addr_io); + return -EBUSY; + } + hc->addr_io = addr_io; + + /* data_io unused for x86 */ + /* hc->data_io = data_io; */ + + return 0; +} + +#endif // __LINUX_SL811_HW_H