📄 l7205.c
字号:
* Get ready to use the L7205 USB Controller. * * Register an interrupt handler and initialize dma. */int udc_init (void){ // This should never happen! if (IO_USBF_REVISION != USBF_REVISION_11) { dbg_udc (0, "incorrect or not found version: %02lx should be %02x", IO_USBF_REVISION, USBF_REVISION_11); return -EINVAL; } // disable the udc just in case udc_disable_interrupts (0); udc_disconnect (); udc_disable (); return 0;}/* * Switch off the L7205 USB Function */void udc_disable (void){ udc_device = NULL; // disable interrupts IO_USBF_INTDIS = USBF_INTDIS_DNSUS | USBF_INTDIS_DNF0ERR | USBF_INTDIS_DNF1OR | USBF_INTDIS_DNF1ERR | USBF_INTDIS_DNF2UR | USBF_INTDIS_DNF2ERR | USBF_INTDIS_DNF3ERR | USBF_INTDIS_DNGRSM | USBF_INTDIS_DNF0RQ | USBF_INTDIS_DNF1RQ | USBF_INTDIS_DNF2RQ | USBF_INTDIS_DNF3RQ | USBF_INTDIS_DNSOF | USBF_INTDIS_DNHRST; // set USBFUNC_EN IO_SYS_CLOCK_ENABLE &= ~SYS_CLOCK_USBFUNC_EN; // reset USB function // IO_USBF_CONTROL |= USBF_CONTROL_FRST; IO_USBF_CONTROL = 0; // turn off clocks#ifdef xCONFIG_IRIS iris_AUXPLL_OFF_for_usb ();#else IO_SYS_CLOCK_ENABLE &= ~(SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN);#endif}/* * initialize L7205 USB Function */void l7205_init (struct usb_device_instance *device){ //int i; int offset; unsigned char *descriptors; // XXX disable everything IO_USBF_CONTROL = 0; udelay (20);#if defined(CONFIG_L7205SDB) /* * Linkup reports that there may be a problem with video conflicting with * USB Function, this will disable it. */ dbg_udc (2, "1. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); // disable LCD power (bit 22) and wait 20MS IO_CLCDCON &= ~0x20000; udelay (100); dbg_udc (2, "2. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); // disable LCD (bit 0) IO_CLCDCON &= ~0x1; dbg_udc (2, "3. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); // disable Vee and backlight bits 1, 2 IO_SYS_CLOCK_ENABLE &= ~0x3; dbg_udc (2, "4. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE);#endif /* * Setup FIRCLK to use internal clock - c.f. 13.1.2 Clocks */ dbg_udc (5, "setting clocks (internal PLL)"); // clear AUXOSCMUX, AUXPLLMUX and AUXPLLMUL bits of CLOCK_AUX IO_SYS_CLOCK_AUX = 0; // clear AUX0SCMUX and set AUXPLLMUL to 13 for 48Mhz IO_SYS_CLOCK_AUX |= SYS_CLOCK_AUXPLLMUL_48; // set FIRAUX_SEL IO_SYS_CLOCK_SELECT |= SYS_CLOCK_FIRAUX_SEL; // set AUXPLL_EN and AUXCLK_EN #ifdef xCONFIG_IRIS iris_AUXPLL_ON_for_usb ();#else IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN;#endif // wait 8us udelay (10); // set USBFUNC_EN to enable clock IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_USBFUNC_EN; // wait 8us udelay (10); // enable USB Function IO_USBF_CONTROL |= USBF_CONTROL_ENBL; udelay (20); // set program modes IO_USBF_CONTROL |= USBF_CONTROL_F1MOD_IO | USBF_CONTROL_F2MOD_IO; // Force USB function core reset // l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST); // Reset and clear all FIFO's // l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR | USBF_CONTROL_F3CLR); // allocate tmp buffer if ((descriptors = kmalloc (USBF_DESCRIPTORS_MAX, GFP_ATOMIC))) { // clear the descriptor space memset (descriptors, 0, USBF_DESCRIPTORS_MAX); // XXX memset((void *)(__IOA(USBF_DESCRIPTORS)), 0, USBF_DESCRIPTORS_MAX); // copy in device, configuration, langid and up to four strings offset = l7205_copy_request (descriptors, device, 0, 0, USB_DESCRIPTOR_TYPE_DEVICE); offset = l7205_copy_request (descriptors, device, USBF_CONFIGBUF1, offset, USB_DESCRIPTOR_TYPE_CONFIGURATION); offset = l7205_copy_descriptor (descriptors, USBF_STRINGBUF0, offset, (struct usb_descriptor *) usbd_get_string (0), 0); offset = l7205_copy_descriptor (descriptors, USBF_STRINGBUF1, offset, (struct usb_descriptor *) usbd_get_string (1), 0); offset = l7205_copy_descriptor (descriptors, USBF_STRINGBUF2, offset, (struct usb_descriptor *) usbd_get_string (2), 0); offset = l7205_copy_descriptor (descriptors, USBF_STRINGBUF3, offset, (struct usb_descriptor *) usbd_get_string (3), 0); offset = l7205_copy_descriptor (descriptors, USBF_STRINGBUF4, offset, (struct usb_descriptor *) usbd_get_string (4), 0); // copy descriptors to shared RAM memcpy ((unsigned char *) __IOA (USBF_DESCRIPTORS), descriptors, USBF_DESCRIPTORS_MAX); kfree (descriptors); } else { // should never happen dbg_udc (0, "cannot malloc descriptors"); } /* * Configuration and Intialization - c.f. 13.1.4 and c.f. 13.1.1 */ IO_USBF_ENDPTBUF0 = USBF_ENDPTBUF0_EP0MSIZE_8; IO_USBF_ENDPTBUF1 = USBF_ENDPTBUF1_EP1TYPE_BULK | 0x20; IO_USBF_ENDPTBUF2 = USBF_ENDPTBUF2_EP2TYPE_BULK | 0x20; IO_USBF_ENDPTBUF3 = USBF_ENDPTBUF3_EP3TYPE_INT; IO_USBF_F1TOUT = 1; IO_USBF_F1BCNT = 0x20; IO_USBF_F2BCNT = 0; // Force USB function core reset l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_FRST); // Reset and clear all FIFO's l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR | USBF_CONTROL_F3CLR); // Clear interrupts IO_USBF_INTCLR = 0x3fff; // set initialization done bit to indicate descriptors and registers are ready IO_USBF_CONTROL |= USBF_CONTROL_INTD; // enable interrupts IO_USBF_INTENA = USBF_INTENA_ENSUS | USBF_INTENA_ENF0ERR | USBF_INTENA_ENF1OR | USBF_INTENA_ENF1ERR | USBF_INTENA_ENF2UR | USBF_INTENA_ENF2ERR | USBF_INTENA_ENF3ERR | USBF_INTENA_ENGRSM | USBF_INTENA_ENF0RQ | USBF_INTENA_ENF1RQ | USBF_INTENA_ENF2RQ | USBF_INTENA_ENF3RQ | USBF_INTENA_ENSOF | USBF_INTENA_ENHRST; // wait udelay (60); // XXX l7205_regs ("finished");}/* * Switch on the L7205 USB Function */void udc_enable (struct usb_device_instance *device){ udc_device = device; l7205_init (udc_device); // XXX do when Vbus is present.... return;}#if defined(TRIGGER)static unsigned int max_triggers = 64;static void send_usb_trigger (unsigned int status, char *id){ /* This code is to insert a "trigger" into the outgoing data stream so that the a USB protocol anlyser can capture the data surrounding it. It is only used when debugging strange hardware states. The "trigger" is data that looks like "0xdeadbeef". */ if (max_triggers == 0) { printk (KERN_DEBUG "%s & no more triggers\n", id); return; } if (status & USBF_STATUS_F2BSY) { printk (KERN_DEBUG "%s & F2BSY\n", id); } else { static char flag_bytes[48] = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef }; unsigned long *slp; unsigned long *txf = IO_USBF_FIFO_TX; int i, j, fifo_good; slp = (unsigned long *) (void *) (0xfffffffc & (unsigned long) (3 + (void *) flag_bytes)); j = 0; do { for (i = 0; i < 8; i++) { IO_USBF_FIFO2 = slp[i]; } fifo_good = 1; for (i = 0; i < 8; i++) { fifo_good &= (slp[i] == txf[i]); } } while (++j < 10 && !fifo_good); if (fifo_good) { IO_USBF_F2BCNT = 32; printk (KERN_DEBUG "deadbeef %u %s\n", max_triggers, id); max_triggers -= 1; } else { // too busy receiving? printk (KERN_DEBUG "%s & F1BSY\n", id); } }}#endif#define STATUS_NO_INTR_MASK ~(USBF_STATUS_F1NE | USBF_STATUS_F2NF | USBF_STATUS_F2NE | USBF_STATUS_F2BSY | USBF_STATUS_VCCMD);/** * udc_int_hndlr - interrupt handler * */void udc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs){ int loopcount; unsigned int rawstatus; int ep2_int_hndlr_called = 0; udc_interrupts++; // XXX rawstatus = IO_USBF_RAWSTATUS; for (loopcount = 0; 0 != (rawstatus = IO_USBF_RAWSTATUS) && (loopcount < 4); loopcount++) { unsigned int status = IO_USBF_STATUS; IO_USBF_INTCLR = rawstatus; /* * Handle high priority interrupts first to reduce latency and * overhead. */ if (rawstatus & USBF_RAWSTATUS_RF1RQ) { ep1_int_hndlr (); sof_saw_rx_f1ne = 0; } if (rawstatus & USBF_RAWSTATUS_RF2RQ) { ep2_int_hndlr (status); ep2_int_hndlr_called++; sof_saw_tx_active = 0; } /* * check if we have one of the un-common interrupts, this is faster * than testing for each one sequentially. Note that we will do the * SOF test first, and then another general test for the really * un-common interrupts. */ if (rawstatus & (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR | USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR | USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ | USBF_RAWSTATUS_RHRST | USBF_RAWSTATUS_RSOF)) { /* * SOF - Start of frame interrupt, use as a safe place to check * for missed transmit, receive interrupts, turn back on SUS or * RST interrupts etc. */ if (rawstatus & USBF_RAWSTATUS_RSOF) { /* * Simulate an rx interrupt, if we have not seen a rcv * interrupt recently. */ if (IO_USBF_STATUS & USBF_STATUS_F1NE) { if ((sof_saw_rx_f1ne++ > 2)) { dbg_tick (1, "[%d]: F1NE %ld", udc_interrupts, IO_USBF_F1BCNT); ep1_clear (); sof_saw_rx_f1ne = 0; } } else { sof_saw_rx_f1ne = 0; } /* * This is required in case we miss a tx interrupt or we * abandoned a tx attempt due to contention with rx fifo */ if ((sof_saw_tx_active++ > 10) && ep2_active) { ep2_int_hndlr_error (status, 0, ep2_int_hndlr_called++); sof_saw_tx_active = 0; } /* * The SUS and HRST interrupts disable themselves, now is * the time to re-enable them. */ if (host_sus_interrupt_disabled) { IO_USBF_INTENA = USBF_INTENA_ENSUS; host_sus_interrupt_disabled = 0; usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); } if (host_reset_interrupt_disabled) { IO_USBF_INTENA = USBF_INTENA_ENHRST; host_reset_interrupt_disabled = 0; usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); udc_device->configuration = 0; usbd_device_event (udc_device, DEVICE_CONFIGURED, 0); udc_device->interface = 0; udc_device->alternate = 0; usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0); } /* * If these three bits are on all is lost, we will * disconnect. */ if ((status & USBF_STATUS_F2BSY) && (status & USBF_STATUS_F2NE) && (status & USBF_STATUS_F2NF)) { // disconnect and reset udc // XXX udc_disconnect(); // XXX udc_connect(); } } /* * check if we have a really uncommon interrupt, this saves * sequential testing of all of these during SOF only * interrupts. */ if (rawstatus & (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR | USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR | USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ | USBF_RAWSTATUS_RHRST)) { /* * SUS - suspend interrupt, issued device event and disable SUS interrupt * until we see an SOF interrupt */ if (rawstatus & USBF_RAWSTATUS_RSUS) { dbg_intr (0, "[%u]: SUS Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); // disable host interrupt IO_USBF_INTDIS = USBF_INTDIS_DNSUS; host_sus_interrupt_disabled++; usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); } /* * HRST - Host Reset interrupt, issued device event and disable SUS interrupt * until we see an SOF interrupt */ if (rawstatus & USBF_RAWSTATUS_RHRST) { // L7210 AB version would give multiple HRST when the first is cleared, // ignore them until SOF interrupt dbg_intr (0, "[%u]: HRST", udc_interrupts); // XXX l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST); // reset udc l7205_init (udc_device); // XXX // disable host interrupt IO_USBF_INTDIS = USBF_INTDIS_DNHRST; host_reset_interrupt_disabled++; usbd_device_event (udc_device, DEVICE_RESET, 0); } /* * F1ERR - FIFO 1 error, a receive error, bad news, usually once * this has happened we cannot continue or reset to normal * state. In desparation we will drop connection and hope that * HRST will restore things. */ if (rawstatus & USBF_RAWSTATUS_RF1ERR) { dbg_intr (0, "[%u]: F1ERR Sts: %08x Raw: %08x F1Bcnt: %ld", udc_interrupts, status, rawstatus, IO_USBF_F1BCNT); sof_saw_rx_f1ne = 0;#if defined(FLAG_F1ERR) && defined(TRIGGER) send_usb_trigger (status, "F1ERR");#endif udc_disconnect (); ep1_int_hndlr_error (); udelay (100); udc_connect (); udelay (100); udc_disconnect (); // XXX udc_disconnect(); // XXX l7205_init(udc_device); // XXX } /* * Misc Error interrupts - have never seen any of these. */ if (rawstatus & USBF_RAWSTATUS_RF0ERR) { dbg_intr (0, "[%u]: F0ERR Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); } if (rawstatus & USBF_RAWSTATUS_RF1OR) { dbg_intr (0, "[%u]: F1OR Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); ep1_int_hndlr_error (); sof_saw_rx_f1ne = 0; } if (rawstatus & USBF_RAWSTATUS_RF2UR) { dbg_intr (0, "[%u]: F2UR Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); sof_saw_tx_active = 0; ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++); } if (rawstatus & USBF_RAWSTATUS_RF2ERR) { dbg_intr (0, "[%u]: F2ERR Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); sof_saw_tx_active = 0; ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++); } if (rawstatus & USBF_RAWSTATUS_RF3ERR) { dbg_intr (0, "[%u]: F3ERR Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); } if (rawstatus & USBF_RAWSTATUS_RGRSM) { dbg_intr (0, "[%u]: GRSM Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); } if (rawstatus & USBF_RAWSTATUS_RF0RQ) { dbg_intr (0, "[%u]: F0RQ Sts: %08x Raw: %08x", udc_interrupts, status, rawstatus); } if (rawstatus & USBF_RAWSTATUS_RF3RQ) { dbg_intr (0, "[%u]: F3RQ Sts: %08x Raw: %08x",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -