/* Rx/OUT interrupt handler */ void usb_stream_rx(struct usb_stream_config const *config) { /* Wake up the Rx FIFO handler */ hook_call_deferred(config->deferred_rx, 0); GR_USB_DOEPINT(config->endpoint) = 0xffffffff; }
/* Rx/OUT interrupt handler */ static void con_ep_rx(void) { /* Wake up the Rx FIFO handler */ hook_call_deferred(rx_fifo_handler, 0); /* clear the RX/OUT interrupts */ GR_USB_DOEPINT(USB_EP_BLOB) = 0xffffffff; }
/* Rx/OUT interrupt handler */ static void con_ep_rx(void) { struct dwc_usb_ep *ep = &ep_console_ctl; if (GR_USB_DOEPCTL(USB_EP_CONSOLE) & DXEPCTL_EPENA) return; /* Bytes received decrement DOEPTSIZ XFERSIZE */ if (GR_USB_DOEPINT(USB_EP_CONSOLE) & DOEPINT_XFERCOMPL) { ep->out_pending = ep->max_packet - (GR_USB_DOEPTSIZ(USB_EP_CONSOLE) & GC_USB_DOEPTSIZ1_XFERSIZE_MASK); } /* Wake up the Rx FIFO handler */ hook_call_deferred(&rx_fifo_handler_data, 0); /* clear the RX/OUT interrupts */ GR_USB_DOEPINT(USB_EP_CONSOLE) = 0xffffffff; }
/* This handles both IN and OUT interrupts for EP0 */ static void ep0_interrupt(uint32_t intr_on_out, uint32_t intr_on_in) { uint32_t doepint, diepint; enum table_case tc; int sr; /* Determine the interrupt cause and clear the bits quickly, but only * if they really apply. I don't think they're trustworthy if we didn't * actually get an interrupt. */ doepint = GR_USB_DOEPINT(0); if (intr_on_out) GR_USB_DOEPINT(0) = doepint; diepint = GR_USB_DIEPINT(0); if (intr_on_in) GR_USB_DIEPINT(0) = diepint; print_later("doepint%c 0x%08x diepint%c 0x%08x what %d", intr_on_out ? '!' : '_', doepint, intr_on_in ? '!' : '_', diepint, what_am_i_doing); /* Update current and pending RX FIFO buffers */ if (intr_on_out && (doepint & DOEPINT_XFERCOMPL)) got_RX_packet(); /* Decode the situation according to Table 10-7 */ tc = decode_table_10_7(doepint); sr = cur_out_desc->flags & DOEPDMA_SR; print_later("cur_out_idx %d flags 0x%08x case=%c SR=%d", cur_out_idx, cur_out_desc->flags, "0ABCDE67"[tc], !!sr, 0); switch (what_am_i_doing) { case WAITING_FOR_SETUP_PACKET: if (tc == TABLE_CASE_A || tc == TABLE_CASE_C) { if (sr) { handle_setup(tc); } else { report_error(tc); print_later("next_out_idx %d flags 0x%08x", next_out_idx, next_out_desc->flags, 0, 0, 0); expect_setup_packet(); } } /* This only happens if we're stalling, so keep doing it. */ if (tc == TABLE_CASE_B) { print_later("Still waiting for Setup...", 0, 0, 0, 0, 0); stall_both_fifos(); } break; case DATA_STAGE_IN: if (intr_on_in && (diepint & DIEPINT_XFERCOMPL)) { print_later("IN is complete? Maybe? How do we know?", 0, 0, 0, 0, 0); /* I don't *think* we need to do this, unless we need * to transfer more data. Customer support agrees and * it shouldn't matter if the host is well-behaved, but * it seems like we had issues without it. * TODO: Test this case until we know for sure. */ GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; /* * The Programmer's Guide says (p291) to stall any * further INs, but that's stupid because it'll destroy * the packet we just transferred to SPRAM, so don't do * that (we tried it anyway, and Bad Things happened). * Also don't stop here, but keep looking at stuff. */ } /* But we should ignore the OUT endpoint if we didn't actually * get an OUT interrupt. */ if (!intr_on_out) break; if (tc == TABLE_CASE_B) { print_later("IN has been detected...", 0, 0, 0, 0, 0); /* The first IN packet has been seen. Keep going. */ GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; break; } if (tc == TABLE_CASE_A) { if (!sr) { /* We've handled the Status phase. All done. */ print_later("Status phase complete", 0, 0, 0, 0, 0); expect_setup_packet(); break; } /* We expected an OUT, but got a Setup. Deal with it. */ print_later("Early Setup", 0, 0, 0, 0, 0); handle_setup(tc); break; } /* From the Exceptional Control Read Transfer section ... */ if (tc == TABLE_CASE_C) { if (sr) { print_later("Early Setup w/Data packet seen", 0, 0, 0, 0, 0); handle_setup(tc); break; } print_later("Status phase complete. I think...", 0, 0, 0, 0, 0); expect_setup_packet(); break; } /* Anything else should be ignorable. Right? */ break; case NO_DATA_STAGE: if (intr_on_in && (diepint & DIEPINT_XFERCOMPL)) { print_later("IN descriptor processed", 0, 0, 0, 0, 0); /* Let the IN proceed */ GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; } /* Done unless we got an OUT interrupt */ if (!intr_on_out) break; if (tc == TABLE_CASE_B) { print_later("IN has been detected...", 0, 0, 0, 0, 0); /* Let the IN proceed */ GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; /* Reenable the previously prepared OUT descriptor. */ GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; break; } if (tc == TABLE_CASE_A || tc == TABLE_CASE_C) { if (sr) { /* We expected an IN, but got a Setup. */ print_later("Early Setup", 0, 0, 0, 0, 0); handle_setup(tc); break; } } /* Anything else means get ready for a Setup packet */ print_later("Status phase complete. Maybe.", 0, 0, 0, 0, 0); expect_setup_packet(); break; } }
void usb_init(void) { int i, resume; /* USB is in use */ disable_sleep(SLEEP_MASK_USB_DEVICE); /* * Resuming from a deep sleep is a lot like a cold boot, but there are * few things that we need to do slightly differently. However, we ONLY * do them if we're really resuming due to a USB wakeup. If we're woken * for some other reason, we just do a normal USB reset. The host * doesn't mind. */ resume = ((system_get_reset_flags() & RESET_FLAG_USB_RESUME) && (GR_USB_GINTSTS & GC_USB_GINTSTS_WKUPINT_MASK)); /* TODO(crosbug.com/p/46813): Clean this up. Do only what's needed, and * use meaningful constants instead of magic numbers. */ GREG32(GLOBALSEC, DDMA0_REGION0_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION1_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION2_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DDMA0_REGION3_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION0_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION1_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION2_CTRL) = 0xffffffff; GREG32(GLOBALSEC, DUSB0_REGION3_CTRL) = 0xffffffff; /* Enable clocks */ clock_enable_module(MODULE_USB, 1); /* TODO(crbug.com/496888): set up pinmux */ gpio_config_module(MODULE_USB, 1); /* Make sure interrupts are disabled */ GR_USB_GINTMSK = 0; GR_USB_DAINTMSK = 0; GR_USB_DIEPMSK = 0; GR_USB_DOEPMSK = 0; /* Select the correct PHY */ usb_select_phy(which_phy); /* Full-Speed Serial PHY */ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN | GUSBCFG_TOUTCAL(7) /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ | GUSBCFG_USBTRDTIM(14); if (!resume) /* Don't reset on resume, because some preserved internal state * will be lost and there's no way to restore it. */ usb_softreset(); GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN | GUSBCFG_TOUTCAL(7) /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ | GUSBCFG_USBTRDTIM(14); /* Global + DMA configuration */ /* TODO: What about the AHB Burst Length Field? It's 0 now. */ GR_USB_GAHBCFG = GAHBCFG_DMA_EN | GAHBCFG_GLB_INTR_EN | GAHBCFG_NP_TXF_EMP_LVL; /* Be in disconnected state until we are ready */ if (!resume) usb_disconnect(); if (resume) /* DEVADDR is preserved in the USB module during deep sleep, * but it doesn't show up in USB_DCFG on resume. If we don't * restore it manually too, it doesn't work. */ GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18); else /* Init: USB2 FS, Scatter/Gather DMA, DEVADDR = 0x00 */ GR_USB_DCFG |= DCFG_DEVSPD_FS48 | DCFG_DESCDMA; /* If we've restored a nonzero device address, update our state. */ if (GR_USB_DCFG & GC_USB_DCFG_DEVADDR_MASK) { /* Caution: We only have one config TODAY, so there's no real * difference between DS_CONFIGURED and DS_ADDRESS. */ device_state = DS_CONFIGURED; configuration_value = 1; } else { device_state = DS_DEFAULT; configuration_value = 0; } /* Now that DCFG.DesDMA is accurate, prepare the FIFOs */ setup_data_fifos(); /* If resuming, reinitialize the endpoints now. For a cold boot we'll * do this as part of handling the host-driven reset. */ if (resume) usb_init_endpoints(); /* Clear any pending interrupts */ for (i = 0; i < 16; i++) { GR_USB_DIEPINT(i) = 0xffffffff; GR_USB_DOEPINT(i) = 0xffffffff; } GR_USB_GINTSTS = 0xFFFFFFFF; /* Unmask some endpoint interrupt causes */ GR_USB_DIEPMSK = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK; GR_USB_DOEPMSK = DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK | DOEPMSK_SETUPMSK; /* Enable interrupt handlers */ task_enable_irq(GC_IRQNUM_USB0_USBINTR); /* Allow USB interrupts to come in */ GR_USB_GINTMSK = /* NAK bits that must be cleared by the DCTL register */ GINTMSK(GOUTNAKEFF) | GINTMSK(GINNAKEFF) | /* Initialization events */ GINTMSK(USBRST) | GINTMSK(ENUMDONE) | /* Endpoint activity, cleared by the DOEPINT/DIEPINT regs */ GINTMSK(OEPINT) | GINTMSK(IEPINT) | /* Reset detected while suspended. Need to wake up. */ GINTMSK(RESETDET) | /* TODO: Do we need this? */ /* Idle, Suspend detected. Should go to sleep. */ GINTMSK(ERLYSUSP) | GINTMSK(USBSUSP) | /* Watch for first SOF */ GINTMSK(SOF); /* Device registers have been setup */ GR_USB_DCTL |= DCTL_PWRONPRGDONE; udelay(10); GR_USB_DCTL &= ~DCTL_PWRONPRGDONE; /* Clear global NAKs */ GR_USB_DCTL |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; #ifndef CONFIG_USB_INHIBIT_CONNECT /* Indicate our presence to the USB host */ if (!resume) usb_connect(); #endif }