www.pudn.com > isp1161.rar > hc_isp116x.c
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------* * isp1161 USB HCD for Linux Version 0.9.5 (10/28/2001) * * requires (includes) hc_simple.[hc] simple generic HCD frontend * * Roman Weissgaerber weissg@vienna.at (C) 2001 * * Thanks to Benjamin Herrenschmidt for debugging and the removing of * some bugs. * *-------------------------------------------------------------------------* * 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 * *-------------------------------------------------------------------------*/ /*History*/ /* V 0.9.5 10/28/2001 correct toggle on error */ /* inw, outw macros; ISOC support */ /* V 0.9 remove some bugs, ISOC still missing */ /* V 0.8.1 9/5/2001 mv memcpy of in packet to hc */ /* V 0.8 initial release */ #include#include #include #include #include #include #include #include #include #include #include #include #include #include #undef HC_URB_TIMEOUT #undef HC_SWITCH_INT #undef HC_ENABLE_ISOC #include "hc_isp116x.h" #include "hc_simple.h" static int hc_verbose = 1; static int hc_error_verbose = 0; static int urb_debug = 0; #include "hc_simple.c" #include "hc_isp116x_rh.c" #ifndef CONFIG_ARCH_SITSANG static int irq = 10; static int hcport = 0x290; static int hcport2 = 0x292; static int wuport = 0x240; #else static int irq = SITSANG_USB_HC_IRQ; static int hcport = 0xf4000290; static int hcport2 = 0xf4000292; #endif MODULE_PARM(hc_verbose,"i"); MODULE_PARM_DESC(hc_verbose,"verbose startup messages, default is 1 (yes)"); MODULE_PARM(hc_error_verbose,"i"); MODULE_PARM_DESC(hc_error_verbose,"verbose error/warning messages, default is 0 (no)"); MODULE_PARM(urb_debug,"i"); MODULE_PARM_DESC(urb_debug,"debug urb messages, default is 0 (no)"); MODULE_PARM(irq,"i"); MODULE_PARM_DESC(irq,"IRQ 3, 10 (default)"); MODULE_PARM(hcport,"i"); MODULE_PARM_DESC(hcport,"ISP116x PORT 0x290"); MODULE_PARM(hcport2,"i"); MODULE_PARM_DESC(hcport2,"ISP116x PORT2 0x292"); static inline void ISP116x_OUTW (int val, int addr) { outw (val, addr); } static inline int ISP116x_INW (int addr) { return inw (addr); } static inline int READ_REG32 (hci_t * hci, int regindex) { hcipriv_t * hp = &hci->hp; int val16, val; ISP116x_OUTW (regindex, hp->hcport2); val16 = ISP116x_INW (hp->hcport); val = val16; val16 = ISP116x_INW (hp->hcport); val += val16 << 16; return val; } static inline int READ_REG16 (hci_t * hci, int regindex) { hcipriv_t * hp = &hci->hp; int val = 0; ISP116x_OUTW (regindex, hp->hcport2); val = ISP116x_INW (hp->hcport); return val; } static inline void READ_REGn16 (hci_t * hci, int regindex, int length, __u8 * buffer) { hcipriv_t * hp = &hci->hp; int i; int val = 0; ISP116x_OUTW (regindex, hp->hcport2); for (i = 0; i < length - 1; i += 2) { val = ISP116x_INW (hp->hcport); buffer [i] = val; buffer [i+1] = val >> 8; } if (length & 1) { val = ISP116x_INW (hp->hcport); buffer [length - 1] = val; } #ifdef DEBUG1 printk (" READ_REGn16: %d :", length); for (i = 0; i < length; i++) printk (" %2x", buffer [i]); printk("\n"); #endif } static inline void WRITE_REG32 (hci_t * hci, unsigned int value, int regindex) { hcipriv_t * hp = &hci->hp; ISP116x_OUTW (regindex | 0x80, hp->hcport2); ISP116x_OUTW (value, hp->hcport); ISP116x_OUTW (value >> 16, hp->hcport); } static inline void WRITE_REG16 (hci_t * hci, unsigned int value, int regindex) { hcipriv_t * hp = &hci->hp; ISP116x_OUTW (regindex | 0x80, hp->hcport2); ISP116x_OUTW (value, hp->hcport); } static inline void WRITE_REG0 (hci_t * hci, int regindex) { hcipriv_t * hp = &hci->hp; ISP116x_OUTW (regindex | 0x80, hp->hcport2); } static inline void WRITE_REGn16 (hci_t * hci, int regindex, int length, __u8 * buffer) { hcipriv_t * hp = &hci->hp; int i; ISP116x_OUTW (regindex | 0x80, hp->hcport2); for (i = 0; i < length - 1; i += 2) { ISP116x_OUTW (buffer [i] + (buffer [i+1] << 8), hp->hcport); } if (length & 1) { ISP116x_OUTW (buffer [length - 1], hp->hcport); } #ifdef DEBUG1 printk (" WRITE_REGn16: %d :", length); for (i = 0; i < length; i++) printk (" %2x", buffer [i]); printk("\n"); #endif } /*-------------------------------------------------------------------------*/ /* tl functions */ static inline void hc_mark_last_trans (hci_t * hci) { hcipriv_t * hp = &hci->hp; __u8 * ptd = hp->tl; if (hp->tlp > 0) *(ptd + hp->tl_last) |= (1 << 3); } static inline void hc_flush_data_cache (hci_t * hci, void * data, int len) {} 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) { hcipriv_t * hp = &hci->hp; int last = 0; __u8 * ptd = hp->tl; int parts = 2; /* just send 4 packets of each kind at a frame, * other URBs also want some bandwitdh, and a NACK is cheaper * with less packets */ switch (hci->active_urbs) { case 1: parts = 8; break; case 2: parts = 6; break; case 3: parts = 4; break; case 4: parts = 3; break; case 5: parts = 2; break; default: parts = 2; } if (len > maxps * parts) len = maxps * parts; if (hp->units_left < ((len + 8 + 3) & ~0x3)) return -1; else hp->units_left -= (len + 8 + 3) & ~0x3; ptd += hp->tlp; hp->tl_last = hp->tlp + 3; ptd [0] = 0; ptd [1] = (toggle << 2) | (1 << 3) | (0xf << 4); ptd [2] = maxps; ptd [3] = ((maxps >> 8) & 0x3) | (slow << 2) | (last << 3) | (endpoint << 4); ptd [4] = len; ptd [5] = ((len >> 8) & 0x3) | (pid << 2); ptd [6] = address | (format << 7); ptd [7] = 0; if (pid != PID_IN && len) memcpy (ptd + 8, data, len); hp->tlp += ((len + 8 + 3) & ~0x3); return len; } static inline int hc_parse_trans (hci_t * hci, int * actbytes, void * data, int * cc, int * toggle, int length) { hcipriv_t * hp = &hci->hp; int last = 0; int totbytes; int pid; __u8 *ptd = hp->tl + hp->tlp; *cc = (ptd [1] >> 4) & 0xf; last = (ptd [3] >> 3) & 0x1; *actbytes = ((ptd [1] & 0x3) << 8) | ptd [0]; totbytes = ((ptd [5] & 0x3) << 8) | ptd [4]; pid = (ptd [5] >> 2) & 0x3; if (*actbytes > length) *actbytes = length; if (pid == PID_IN && *actbytes) memcpy (data, ptd + 8, *actbytes); *toggle = (ptd [1] >> 2 & 1); if (*cc > 0 && *cc < 0xE && *cc != 9) *toggle = !*toggle; hp->tlp += ((totbytes + 8 + 3) & ~0x3); return !last; } /*-------------------------------------------------------------------------*/ static void hc_start_int (hci_t * hci) { #ifdef HC_SWITCH_INT int mask = SOFITLInt | ATLInt | OPR_Reg; WRITE_REG16 (hci, mask, HcuPInterrupt); WRITE_REG16 (hci, mask, HcuPInterruptEnable); #endif } static void hc_stop_int (hci_t * hci) { #ifdef HC_SWITCH_INT WRITE_REG16 (hci, 0, HcuPInterruptEnable); #endif } /* an interrupt happens */ static void hc_interrupt (int irq, void * __hci, struct pt_regs * r) { hci_t * hci = __hci; hcipriv_t * hp = &hci->hp; int ints_uP, ints = 0, bstat = 0; int iso_buffer_index = 0; /* #ifdef CONFIG_ARCH_SITSANG if (!(SITSANG_BIPR_RW & SITSANG_BIPR_USB_HC_IRQ)) return ; SITSANG_BIMR_RW &= ~SITSANG_BIMR_USB_HC_IRQ; SITSANG_BIPR_RW = SITSANG_BIPR_USB_HC_IRQ; #endif */ if ((ints_uP = (READ_REG16 (hci, HcuPInterrupt) & READ_REG16 (hci, HcuPInterruptEnable))) == 0) { return; } WRITE_REG16 (hci, ints_uP, HcuPInterrupt); if ((ints_uP & OPR_Reg) && (ints = (READ_REG32 (hci, HcInterruptStatus)))) { if (ints & OHCI_INTR_SO) { dbg("USB Schedule overrun"); WRITE_REG32 (hci, OHCI_INTR_SO, HcInterruptEnable); } if (ints & OHCI_INTR_SF) { WRITE_REG32 (hci, OHCI_INTR_SF, HcInterruptEnable); } WRITE_REG32 (hci, ints, HcInterruptStatus); WRITE_REG32 (hci, OHCI_INTR_MIE, HcInterruptEnable); } hci->frame_number = GET_FRAME_NUMBER (hci); bstat = READ_REG16 (hci, HcBufferStatus); if (ints_uP & SOFITLInt) { hci->frame_number = GET_FRAME_NUMBER (hci); #ifdef HC_ENABLE_ISOC /* read itl1 first. if there is an buffer overflow HC will stop at itl1 There will be a quirks in sh_done_list if this happens !!!!! */ if ((bstat & ITL1BufferFull) && (bstat & ITL1BufferDone)) { hp->itl1_len = READ_REG16 (hci, HcReadBackITL1Length); if (hp->itl1_len > 0) { WRITE_REG16 (hci, hp->itl1_len, HcTransferCounter); READ_REGn16 (hci, HcITLBufferPort, hp->itl1_len, hp->tl); hp->tlp = 0; hci->td_array = &hci->i_td_array [1]; sh_done_list (hci); } iso_buffer_index = 1; } if ((bstat & ITL0BufferFull) && (bstat & ITL0BufferDone)) { hp->itl0_len = READ_REG16 (hci, HcReadBackITL0Length); if (hp->itl0_len > 0) { WRITE_REG16 (hci, hp->itl0_len, HcTransferCounter); READ_REGn16 (hci, HcITLBufferPort, hp->itl0_len, hp->tl); hp->tlp = 0; hci->td_array = &hci->i_td_array [0]; sh_done_list (hci); } iso_buffer_index = 0; } hp->tlp = 0; hci->td_array = &hci->i_td_array [iso_buffer_index]; sh_scan_iso_urb_list (hci, &hci->iso_list, hci->frame_number + 1); if (hp->tlp >0) { WRITE_REG16 (hci, hp->tlp, HcTransferCounter); WRITE_REGn16 (hci, HcITLBufferPort, hp->tlp, hp->tl); } #endif } if (ints_uP & ATLInt) { if ((bstat & ATLBufferFull) && (bstat & ATLBufferDone)) { if (hp->atl_len > 0) { WRITE_REG16 (hci, hp->atl_len, HcTransferCounter); READ_REGn16 (hci, HcATLBufferPort, hp->atl_len, hp->tl); hp->tlp = 0; hci->td_array = &hci->a_td_array; sh_done_list (hci); } hp->tlp = 0; } } if (!hci->td_array->len) sh_del_list (hci); hci->td_array = &hci->a_td_array; bstat = READ_REG16 (hci, HcBufferStatus); if (hci->td_array->len == 0 && !(bstat & ATLBufferFull)) { hp->units_left = hp->atl_buffer_len; hp->tlp = 0; sh_schedule_trans (hci); hc_mark_last_trans (hci); hp->atl_len = hp->tlp; if (hp->atl_len > 0) { WRITE_REG16 (hci, hp->atl_len, HcTransferCounter); WRITE_REGn16 (hci, HcATLBufferPort, hp->atl_len, hp->tl); } } /* #ifdef CONFIG_ARCH_SITSANG SITSANG_BIPR_RW = SITSANG_BIPR_USB_HC_IRQ; SITSANG_BIMR_RW |= SITSANG_BIMR_USB_HC_IRQ; #endif */ } /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ /* reset the HC and BUS */ static int hc_reset (hci_t * hci) { int timeout = 30; /* Disable HC interrupts */ WRITE_REG32 (hci, OHCI_INTR_MIE, HcInterruptDisable); dbg ("USB HC reset_hc usb-: ctrl = 0x%x ;", READ_REG32 (hci, HcControl)); /* Reset USB (needed by some controllers) */ WRITE_REG32 (hci, 0, HcControl); WRITE_REG0 (hci, HcSoftwareReset); /* HC Reset requires max 10 us delay */ WRITE_REG32 (hci, OHCI_HCR, HcCommandStatus); while ((READ_REG32 (hci, HcCommandStatus) & OHCI_HCR) != 0) { if (--timeout == 0) { err ("USB HC reset timed out!"); return -1; } udelay (1); } return 0; } /*-------------------------------------------------------------------------*/ /* Start an host controller, set the BUS operational * enable interrupts * connect the virtual root hub */ 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 */ WRITE_REG16 (hci, hp->itl_buffer_len, HcITLBufferLength); WRITE_REG16 (hci, hp->atl_buffer_len, HcATLBufferLength); WRITE_REG16 (hci, InterruptPinEnable | InterruptPinTrigger | InterruptOutputPolarity | DataBusWidth16 | AnalogOCEnable, HcHardwareConfiguration); WRITE_REG16 (hci, 0, HcDMAConfiguration); 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; } static int hc_start (hci_t * hci) { int uData; hcipriv_t * hp = &hci->hp; __u32 mask; unsigned int fminterval; fminterval = 0x2edf; fminterval |= ((((fminterval - 210) * 6) / 7) << 16); WRITE_REG32 (hci, fminterval, HcFmInterval); WRITE_REG32 (hci, 0x628, HcLSThreshold); /*start the usb as operational*/ hp->hc_control = OHCI_USB_OPER; WRITE_REG32 (hci, hp->hc_control, HcControl); /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_MIE | OHCI_INTR_SO | OHCI_INTR_SF; WRITE_REG32 (hci, mask, HcInterruptEnable); WRITE_REG32 (hci, mask, HcInterruptStatus); mask = SOFITLInt | ATLInt | OPR_Reg; #ifdef HC_SWITCH_INT mask = 0; #endif WRITE_REG16 (hci, mask, HcuPInterrupt); WRITE_REG16 (hci, mask, HcuPInterruptEnable); //Use NPS according to Philip ISA-USB driver demo ,11/20/2002 uData = READ_REG32(hci,HcRhDescriptorA); uData &= ~RH_A_POTPGT; uData |= RH_A_NPS; uData &= ~RH_A_PSM; uData |= ((50/2)<<24); WRITE_REG32 (hci,uData,HcRhDescriptorA); WRITE_REG32 (hci, RH_HS_LPSC, HcRhStatus); // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((READ_REG32 (hci, HcRhDescriptorA) >> 23) & 0x1fe); rh_connect_rh (hci); return 0; } /*-------------------------------------------------------------------------*/ /* allocate HCI */ static hci_t * __devinit hc_alloc_hci (void) { hci_t * hci; hcipriv_t * hp; struct usb_bus * bus; 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; 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; 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->hcpriv = (void *) hci; return hci; } /*-------------------------------------------------------------------------*/ /* De-allocate all resources.. */ static void hc_release_hci (hci_t * hci) { hcipriv_t * hp = &hci->hp; dbg ("USB HC release hci %d", hci->regs); /* disconnect all devices */ if (hci->bus->root_hub) usb_disconnect (&hci->bus->root_hub); if (hp->hcport > 0) { WRITE_REG16 (hci, 0, HcHardwareConfiguration); WRITE_REG16 (hci, 0, HcDMAConfiguration); WRITE_REG16 (hci, 0, HcuPInterruptEnable); } // if (!hci->disabled) hc_reset (hci); if (hp->tl) kfree (hp->tl); if (hp->hcport > 0) { release_region (hp->hcport, 2); hp->hcport = 0; } if (hp->hcport2 > 0) { release_region (hp->hcport2, 2); hp->hcport2 = 0; } if (hp->irq >= 0) { free_irq (hp->irq, hci); hp->irq = -1; } if (hci->bus->busnum >= 0) 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); } /*-------------------------------------------------------------------------*/ /* Increment the module usage count, start the control thread and * return success. */ static int __devinit hc_found_hci (int addr, int addr2, int irq, int dma) { int hcfs; hci_t * hci; hcipriv_t * hp; hci = hc_alloc_hci (); if (!hci) { return -ENOMEM; } hp = &hci->hp; if (!request_region (addr, 2, "ISP116x USB HOST")) { err ("request address %d-%d failed", addr, addr+4); hc_release_hci (hci); return -EBUSY; } hp->hcport = addr; if (!request_region (addr2, 2, "ISP116x USB HOST")) { err ("request address %d-%d failed", addr, addr+4); hc_release_hci (hci); return -EBUSY; } hp->hcport2 = addr2; if ((READ_REG16(hci, HcChipID) & 0xff00) != 0x6100) { hc_release_hci (hci); return -ENODEV; } if (hc_reset (hci) < 0) { hc_release_hci (hci); return -ENODEV; } if (hc_alloc_trans_buffer (hci)) { hc_release_hci (hci); return -ENOMEM; } printk(KERN_INFO __FILE__ ": USB ISP116x at %x/%x,IRQ %d Rev. %x ChipID: %x\n", addr, addr2, irq, READ_REG32(hci, HcRevision), READ_REG16(hci, HcChipID)); /* FIXME this is a second HC reset; why?? */ WRITE_REG32 (hci, hp->hc_control = OHCI_USB_RESET, HcControl); wait_ms (1000); usb_register_bus (hci->bus); if (request_irq (irq, hc_interrupt,SA_SHIRQ,"ISP116x", hci) != 0) { err ("request interrupt %d failed", irq); hc_release_hci (hci); return -EBUSY; } hp->irq = irq; if (hc_start (hci) < 0) { err ("can't start usb-%x", addr); hc_release_hci (hci); return -EBUSY; } /* //Enable USB_HC irq report SITSANG_BIPR_RW = SITSANG_BIPR_USB_HC_IRQ; SITSANG_BIMR_RW |= SITSANG_BIMR_USB_HC_IRQ; */ return 0; } /*-------------------------------------------------------------------------*/ static int __init hci_hcd_init (void) { int ret; #ifdef CONFIG_ARCH_SITSANG unsigned int irq_gpio_pin = 0; irq_gpio_pin = IRQ_TO_GPIO_2_80(SITSANG_USB_HC_IRQ); GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin); set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE); /* SITSANG_PCR_RW |= SITSANG_PCR_USB_HOST_ON | SITSANG_PCR_PER_ON; SITSANG_BCR_RW |= SITSANG_BCR_BUS_OPEN | SITSANG_BCR_USB_NDP; SITSANG_BCR_RW |= SITSANG_BCR_USB_HC_RESET; mdelay(500); */ /*Reset the CHIP*/ // SITSANG_BCR_RW &= ~(SITSANG_BCR_USB_HC_RESET); // mdelay(500); MSC2 &= 0xffff0000; MSC2 |= 0x0000fbc9; #endif ret = hc_found_hci (hcport, hcport2, irq, 0); return ret; } /*-------------------------------------------------------------------------*/ static void __exit hci_hcd_cleanup (void) { struct list_head * hci_l; hci_t * hci; 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); } /* #ifdef CONFIG_ARCH_SITSANG SITSANG_BIMR_RW &= ~SITSANG_BIMR_USB_HC_IRQ; SITSANG_PCR_RW &= ~SITSANG_PCR_USB_HOST_ON; #endif */ } module_init (hci_hcd_init); module_exit (hci_hcd_cleanup); MODULE_AUTHOR ("Roman Weissgaerber "); MODULE_DESCRIPTION ("USB ISP116x Host Controller Driver");