| /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "case_closed_debug.h" |
| #include "clock.h" |
| #include "common.h" |
| #include "config.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "init_chip.h" |
| #include "link_defs.h" |
| #include "printf.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| #include "usb_descriptor.h" |
| #include "usb_hw.h" |
| #include "watchdog.h" |
| |
| /****************************************************************************/ |
| /* Debug output */ |
| |
| /* Console output macro */ |
| #define CPRINTS(format, args...) cprints(CC_USB, format, ## args) |
| #define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) |
| |
| #ifndef CONFIG_USB_SERIALNO |
| #define USB_STR_SERIALNO 0 |
| #endif |
| |
| /* This is not defined anywhere else. Change it here to debug. */ |
| #undef DEBUG_ME |
| #ifdef DEBUG_ME |
| /* |
| * For debugging we want to print a bunch of things from within the interrupt |
| * handlers, but if we try it'll 1) stop working, and 2) mess up the timing |
| * that we're trying to measure. Instead we fill a circular buffer with things |
| * to print when we get the chance. The number of args is fixed (a format |
| * string and five uint32_t args), and will be printed a few at a time in a |
| * HOOK_TICK handler. |
| * |
| */ |
| #define MAX_ENTRIES 350 /* Chosen arbitrarily */ |
| static struct { |
| timestamp_t t; |
| const char *fmt; |
| int a0, a1, a2, a3, a4; |
| } stuff_to_print[MAX_ENTRIES]; |
| static int stuff_in, stuff_out, stuff_overflow; |
| |
| /* Call this only from within interrupt handler! */ |
| void print_later(const char *fmt, int a0, int a1, int a2, int a3, int a4) |
| { |
| int next; |
| |
| stuff_to_print[stuff_in].t = get_time(); |
| stuff_to_print[stuff_in].fmt = fmt; |
| stuff_to_print[stuff_in].a0 = a0; |
| stuff_to_print[stuff_in].a1 = a1; |
| stuff_to_print[stuff_in].a2 = a2; |
| stuff_to_print[stuff_in].a3 = a3; |
| stuff_to_print[stuff_in].a4 = a4; |
| |
| next = (stuff_in + 1) % MAX_ENTRIES; |
| if (next == stuff_out) |
| stuff_overflow++; |
| else |
| stuff_in = next; |
| } |
| |
| static void do_print_later(void) |
| { |
| int lines_per_loop = 32; /* too much at once fails */ |
| int copy_of_stuff_in; |
| int copy_of_overflow; |
| |
| interrupt_disable(); |
| copy_of_stuff_in = stuff_in; |
| copy_of_overflow = stuff_overflow; |
| stuff_overflow = 0; |
| interrupt_enable(); |
| |
| if (copy_of_overflow) |
| ccprintf("*** WARNING: %d MESSAGES WERE LOST ***\n", |
| copy_of_overflow); |
| |
| while (lines_per_loop && stuff_out != copy_of_stuff_in) { |
| ccprintf("at %.6ld: ", stuff_to_print[stuff_out].t); |
| ccprintf(stuff_to_print[stuff_out].fmt, |
| stuff_to_print[stuff_out].a0, |
| stuff_to_print[stuff_out].a1, |
| stuff_to_print[stuff_out].a2, |
| stuff_to_print[stuff_out].a3, |
| stuff_to_print[stuff_out].a4); |
| ccprintf("\n"); |
| stuff_out = (stuff_out + 1) % MAX_ENTRIES; |
| lines_per_loop--; |
| } |
| } |
| DECLARE_HOOK(HOOK_TICK, do_print_later, HOOK_PRIO_DEFAULT); |
| |
| /* Debugging stuff to display some registers and bits */ |
| static const char const *deezbits[32] = { |
| [0] = "CURMOD", |
| [1] = "MODEMIS", |
| [2] = "OTGINT", |
| [3] = "SOF", |
| [4] = "RXFLVL", |
| [6] = "GINNAKEFF", |
| [7] = "GOUTNAKEFF", |
| [10] = "ERLYSUSP", |
| [11] = "USBSUSP", |
| [12] = "USBRST", |
| [13] = "ENUMDONE", |
| [14] = "ISOOUTDROP", |
| [15] = "EOPF", |
| [17] = "EPMIS", |
| [18] = "IEPINT", |
| [19] = "OEPINT", |
| [20] = "INCOMPISOIN", |
| [21] = "INCOMPLP", |
| [22] = "FETSUSP", |
| [23] = "RESETDET", |
| [28] = "CONIDSTSCHNG", |
| [30] = "SESSREQINT", |
| [31] = "WKUPINT", |
| }; |
| |
| static void showbits(uint32_t b) |
| { |
| int i; |
| |
| for (i = 0; i < 32; i++) |
| if (b & (1 << i)) { |
| if (deezbits[i]) |
| ccprintf(" %s", deezbits[i]); |
| else |
| ccprintf(" %d", i); |
| } |
| ccprintf("\n"); |
| } |
| |
| static void showregs(void) |
| { |
| ccprintf("GINTSTS: 0x%08x\n", GR_USB_GINTSTS); |
| showbits(GR_USB_GINTSTS); |
| ccprintf("GINTMSK: 0x%08x\n", GR_USB_GINTMSK); |
| showbits(GR_USB_GINTMSK); |
| ccprintf("DAINT: 0x%08x\n", GR_USB_DAINT); |
| ccprintf("DAINTMSK: 0x%08x\n", GR_USB_DAINTMSK); |
| ccprintf("DOEPMSK: 0x%08x\n", GR_USB_DOEPMSK); |
| ccprintf("DIEPMSK: 0x%08x\n", GR_USB_DIEPMSK); |
| ccprintf("DCFG: 0x%08x\n", GR_USB_DCFG); |
| ccprintf("DOEPCTL0: 0x%08x\n", GR_USB_DOEPCTL(0)); |
| ccprintf("DIEPCTL0: 0x%08x\n", GR_USB_DIEPCTL(0)); |
| ccprintf("DOEPCTL1: 0x%08x\n", GR_USB_DOEPCTL(1)); |
| ccprintf("DIEPCTL1: 0x%08x\n", GR_USB_DIEPCTL(1)); |
| ccprintf("DOEPCTL2: 0x%08x\n", GR_USB_DOEPCTL(2)); |
| ccprintf("DIEPCTL2: 0x%08x\n", GR_USB_DIEPCTL(2)); |
| } |
| |
| /* When debugging, print errors as they occur */ |
| #define report_error(val) \ |
| print_later("Unhandled USB event at usb.c line %d: 0x%x", \ |
| __LINE__, val, 0, 0, 0) |
| |
| #else /* Not debugging */ |
| #define print_later(...) |
| #define showregs(...) |
| |
| /* TODO: Something unexpected happened. Figure out how to report & fix it. */ |
| #define report_error(val) \ |
| CPRINTS("Unhandled USB event at %s line %d: 0x%x", \ |
| __FILE__, __LINE__, val) |
| |
| #endif /* DEBUG_ME */ |
| |
| /****************************************************************************/ |
| /* Standard USB stuff */ |
| |
| #ifdef CONFIG_USB_BOS |
| /* v2.10 (vs 2.00) BOS Descriptor provided */ |
| #define USB_DEV_BCDUSB 0x0210 |
| #else |
| #define USB_DEV_BCDUSB 0x0200 |
| #endif |
| |
| #ifndef USB_DEV_CLASS |
| #define USB_DEV_CLASS USB_CLASS_PER_INTERFACE |
| #endif |
| |
| #ifndef CONFIG_USB_BCD_DEV |
| #define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */ |
| #endif |
| |
| /* USB Standard Device Descriptor */ |
| static const struct usb_device_descriptor dev_desc = { |
| .bLength = USB_DT_DEVICE_SIZE, |
| .bDescriptorType = USB_DT_DEVICE, |
| .bcdUSB = USB_DEV_BCDUSB, |
| .bDeviceClass = USB_DEV_CLASS, |
| .bDeviceSubClass = 0x00, |
| .bDeviceProtocol = 0x00, |
| .bMaxPacketSize0 = USB_MAX_PACKET_SIZE, |
| .idVendor = USB_VID_GOOGLE, |
| .idProduct = CONFIG_USB_PID, |
| .bcdDevice = CONFIG_USB_BCD_DEV, |
| .iManufacturer = USB_STR_VENDOR, |
| .iProduct = USB_STR_PRODUCT, |
| .iSerialNumber = USB_STR_SERIALNO, |
| .bNumConfigurations = 1 |
| }; |
| |
| /* USB Configuration Descriptor */ |
| const struct usb_config_descriptor USB_CONF_DESC(conf) = { |
| .bLength = USB_DT_CONFIG_SIZE, |
| .bDescriptorType = USB_DT_CONFIGURATION, |
| .wTotalLength = 0x0BAD, /* number of returned bytes, set at runtime */ |
| .bNumInterfaces = USB_IFACE_COUNT, |
| .bConfigurationValue = 1, /* Caution: hard-coded value */ |
| .iConfiguration = USB_STR_VERSION, |
| .bmAttributes = 0x80 /* Reserved bit */ |
| #ifdef CONFIG_USB_SELF_POWERED /* bus or self powered */ |
| | 0x40 |
| #endif |
| #ifdef CONFIG_USB_REMOTE_WAKEUP |
| | 0x20 |
| #endif |
| , |
| .bMaxPower = (CONFIG_USB_MAXPOWER_MA / 2), |
| }; |
| |
| const uint8_t usb_string_desc[] = { |
| 4, /* Descriptor size */ |
| USB_DT_STRING, |
| 0x09, 0x04 /* LangID = 0x0409: U.S. English */ |
| }; |
| |
| /****************************************************************************/ |
| /* Packet-handling stuff, specific to this SoC */ |
| |
| /* Some internal state to keep track of what's going on */ |
| static enum { |
| WAITING_FOR_SETUP_PACKET, |
| DATA_STAGE_IN, |
| NO_DATA_STAGE, |
| } what_am_i_doing; |
| |
| /* Programmer's Guide, Table 10-7 */ |
| enum table_case { |
| BAD_0, |
| TABLE_CASE_A, |
| TABLE_CASE_B, |
| TABLE_CASE_C, |
| TABLE_CASE_D, |
| TABLE_CASE_E, |
| BAD_6, |
| BAD_7, |
| }; |
| |
| /* |
| * Table 10-7 in the Programmer's Guide decodes OUT endpoint interrupts: |
| * |
| * Case StatusPhseRecvd SetUp XferCompl Description |
| * |
| * A 0 0 1 Out descriptor is updated. Check |
| * its SR bit to see if we got |
| * a SETUP packet or an OUT packet. |
| * B 0 1 0 SIE has seen an IN or OUT packet |
| * following the SETUP packet. |
| * C 0 1 1 Both A & B at once, I think. |
| * Check the SR bit. |
| * D 1 0 0 SIE has seen the host change |
| * direction, implying Status phase. |
| * E 1 0 1 Out descriptor is updated, and |
| * SIE has seen an IN following it. |
| * This is probably the Status phase |
| * for a Control Write, but could be |
| * an early SETUP for a Control Read |
| * instead. Maybe. The documentation |
| * is unclear. Check the SR bit |
| * anyway. |
| */ |
| static enum table_case decode_table_10_7(uint32_t doepint) |
| { |
| enum table_case val = BAD_0; |
| |
| /* Bits: SI, SPD, IOC */ |
| if (doepint & DOEPINT_XFERCOMPL) |
| val += 1; |
| if (doepint & DOEPINT_SETUP) |
| val += 2; |
| if (doepint & DOEPINT_STSPHSERCVD) |
| val += 4; |
| |
| return val; |
| } |
| |
| /* For STATUS/OUT: Use two DMA descriptors, each with one-packet buffers */ |
| #define NUM_OUT_BUFFERS 2 |
| static uint8_t ep0_out_buf[NUM_OUT_BUFFERS][USB_MAX_PACKET_SIZE]; |
| static struct g_usb_desc ep0_out_desc[NUM_OUT_BUFFERS]; |
| static int cur_out_idx; /* latest with xfercompl=1 */ |
| static const struct g_usb_desc *cur_out_desc; |
| static int next_out_idx; /* next packet will go here */ |
| static struct g_usb_desc *next_out_desc; |
| static int processed_update_counter; |
| |
| /* For IN: Several DMA descriptors, all pointing into one large buffer, so that |
| * we can return the configuration descriptor as one big blob. */ |
| #define NUM_IN_PACKETS_AT_ONCE 4 |
| #define IN_BUF_SIZE (NUM_IN_PACKETS_AT_ONCE * USB_MAX_PACKET_SIZE) |
| static uint8_t ep0_in_buf[IN_BUF_SIZE]; |
| static struct g_usb_desc ep0_in_desc[NUM_IN_PACKETS_AT_ONCE]; |
| static struct g_usb_desc *cur_in_desc; |
| |
| /* Overall device state (USB 2.0 spec, section 9.1.1). |
| * We only need a few, though. */ |
| static enum { |
| DS_DEFAULT, |
| DS_ADDRESS, |
| DS_CONFIGURED, |
| } device_state; |
| static uint8_t configuration_value; |
| |
| #ifndef CONFIG_USB_SELECT_PHY_DEFAULT |
| #define CONFIG_USB_SELECT_PHY_DEFAULT USB_SEL_PHY1 |
| #endif |
| |
| /* Default PHY to use */ |
| static uint32_t which_phy = CONFIG_USB_SELECT_PHY_DEFAULT; |
| |
| void usb_select_phy(uint32_t phy) |
| { |
| which_phy = phy; |
| GR_USB_GGPIO = GGPIO_WRITE(USB_CUSTOM_CFG_REG, |
| (USB_PHY_ACTIVE | which_phy)); |
| CPRINTS("USB PHY %c", which_phy == USB_SEL_PHY0 ? 'A' : 'B'); |
| } |
| |
| uint32_t usb_get_phy(void) |
| { |
| return which_phy; |
| } |
| |
| /* Reset all this to a good starting state. */ |
| static void initialize_dma_buffers(void) |
| { |
| int i; |
| |
| print_later("initialize_dma_buffers()", 0, 0, 0, 0, 0); |
| |
| for (i = 0; i < NUM_OUT_BUFFERS; i++) { |
| ep0_out_desc[i].addr = ep0_out_buf[i]; |
| ep0_out_desc[i].flags = DOEPDMA_BS_HOST_BSY; |
| } |
| next_out_idx = 0; |
| next_out_desc = ep0_out_desc + next_out_idx; |
| GR_USB_DOEPDMA(0) = (uint32_t)next_out_desc; |
| /* cur_out_* will be updated when we get the first RX packet */ |
| |
| for (i = 0; i < NUM_IN_PACKETS_AT_ONCE; i++) { |
| ep0_in_desc[i].addr = ep0_in_buf + i * USB_MAX_PACKET_SIZE; |
| ep0_in_desc[i].flags = DIEPDMA_BS_HOST_BSY; |
| } |
| cur_in_desc = ep0_in_desc; |
| GR_USB_DIEPDMA(0) = (uint32_t)(cur_in_desc); |
| }; |
| |
| /* Change the RX descriptors after each SETUP/OUT packet is received so we can |
| * prepare to receive another without losing track of this one. */ |
| static void got_RX_packet(void) |
| { |
| cur_out_idx = next_out_idx; |
| cur_out_desc = ep0_out_desc + cur_out_idx; |
| next_out_idx = (next_out_idx + 1) % NUM_OUT_BUFFERS; |
| next_out_desc = ep0_out_desc + next_out_idx; |
| GR_USB_DOEPDMA(0) = (uint32_t)next_out_desc; |
| } |
| |
| /* Load the EP0 IN FIFO buffer with some data (zero-length works too). Returns |
| * len, or negative on error. */ |
| int load_in_fifo(const void *source, uint32_t len) |
| { |
| uint8_t *buffer = ep0_in_buf; |
| int zero_packet = (len % USB_MAX_PACKET_SIZE) == 0; |
| int d, l; |
| |
| /* Copy the data into our FIFO buffer */ |
| if (len >= IN_BUF_SIZE) { |
| report_error(len); |
| return -1; |
| } |
| if (len) |
| memcpy(buffer, source, len); |
| |
| /* Set up the descriptors */ |
| for (d = l = 0; len >= USB_MAX_PACKET_SIZE; d++) { |
| ep0_in_desc[d].addr = buffer + d * USB_MAX_PACKET_SIZE; |
| ep0_in_desc[d].flags = DIEPDMA_TXBYTES(USB_MAX_PACKET_SIZE); |
| len -= USB_MAX_PACKET_SIZE; |
| l = d; |
| } |
| /* Maybe one short packet left? */ |
| if (len || zero_packet) { |
| ep0_in_desc[d].addr = buffer + d * USB_MAX_PACKET_SIZE; |
| ep0_in_desc[d].flags = DIEPDMA_TXBYTES(len) | DIEPDMA_SP; |
| l = d; |
| } |
| /* Mark the last descriptor as last. */ |
| ep0_in_desc[l].flags |= (DIEPDMA_LAST | DIEPDMA_IOC); |
| |
| /* Point to the first in the chain */ |
| cur_in_desc = ep0_in_desc; |
| |
| return len; |
| } |
| |
| /* Prepare the EP0 OUT FIFO buffer to accept some data. Returns len, or |
| * negative on error. */ |
| int accept_out_fifo(uint32_t len) |
| { |
| /* TODO: This is not yet implemented */ |
| report_error(len); |
| return -1; |
| } |
| |
| static void flush_in_fifo(void) |
| { |
| /* TODO: Programmer's Guide p167 suggests lots more stuff */ |
| GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0) | GRSTCTL_TXFFLSH; |
| while (GR_USB_GRSTCTL & GRSTCTL_TXFFLSH) |
| ; /* timeout? */ |
| } |
| |
| /* We're complaining about something by stalling both IN and OUT packets, |
| * but a SETUP packet will get through anyway, so prepare for it. */ |
| static void stall_both_fifos(void) |
| { |
| print_later("stall_both_fifos()", 0, 0, 0, 0, 0); |
| |
| what_am_i_doing = WAITING_FOR_SETUP_PACKET; |
| |
| next_out_desc->flags = |
| DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
| | DOEPDMA_IOC | DOEPDMA_LAST; |
| |
| /* We don't care about IN packets right now, only OUT. */ |
| GR_USB_DAINTMSK |= DAINT_OUTEP(0); |
| GR_USB_DAINTMSK &= ~DAINT_INEP(0); |
| |
| /* Stall both IN and OUT. The hardware will reset them when the next |
| * SETUP comes along. */ |
| GR_USB_DOEPCTL(0) = DXEPCTL_STALL | DXEPCTL_EPENA; |
| flush_in_fifo(); |
| GR_USB_DIEPCTL(0) = DXEPCTL_STALL | DXEPCTL_EPENA; |
| } |
| |
| /* The next packet from the host should be a Setup packet. Get ready for it. */ |
| static void expect_setup_packet(void) |
| { |
| print_later("expect_setup_packet()", 0, 0, 0, 0, 0); |
| |
| what_am_i_doing = WAITING_FOR_SETUP_PACKET; |
| |
| next_out_desc->flags = |
| DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
| | DOEPDMA_IOC | DOEPDMA_LAST; |
| |
| /* We don't care about IN packets right now, only OUT. */ |
| GR_USB_DAINTMSK |= DAINT_OUTEP(0); |
| GR_USB_DAINTMSK &= ~DAINT_INEP(0); |
| |
| /* Let it run. We might need CNAK if we just got an OUT for status */ |
| GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; |
| } |
| |
| /* The TX FIFO buffer is loaded. Start the Data phase. */ |
| static void expect_data_phase_in(enum table_case tc) |
| { |
| print_later("expect_data_phase_in(%c)", "0ABCDE67"[tc], 0, 0, 0, 0); |
| |
| what_am_i_doing = DATA_STAGE_IN; |
| |
| /* We apparently have to do this every time we transmit anything */ |
| flush_in_fifo(); |
| |
| /* I don't think we have to do this every time, but the Programmer's |
| * Guide says to, so... */ |
| GR_USB_DIEPDMA(0) = (uint32_t)(cur_in_desc); |
| |
| /* Blindly following instructions here, too. */ |
| if (tc == TABLE_CASE_C) |
| GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; |
| else |
| GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; |
| |
| /* |
| * When the IN is done, we expect a zero-length OUT for the status |
| * phase but it could be an early SETUP instead. We'll have to deal |
| * with either one when it arrives. |
| */ |
| next_out_desc->flags = |
| DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
| | DOEPDMA_IOC | DOEPDMA_LAST; |
| |
| /* And here's this jimmy rustler again... */ |
| if (tc == TABLE_CASE_C) |
| GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; |
| else |
| GR_USB_DOEPCTL(0) = DXEPCTL_EPENA; |
| |
| /* Get an interrupt when either IN or OUT arrives */ |
| GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); |
| |
| } |
| |
| static void expect_data_phase_out(enum table_case tc) |
| { |
| print_later("expect_data_phase_out(%c)", "0ABCDE67"[tc], 0, 0, 0, 0); |
| /* TODO: This is not yet supported */ |
| report_error(tc); |
| expect_setup_packet(); |
| } |
| |
| /* No Data phase, just Status phase (which is IN, since Setup is OUT) */ |
| static void expect_status_phase_in(enum table_case tc) |
| { |
| print_later("expect_status_phase_in(%c)", "0ABCDE67"[tc], 0, 0, 0, 0); |
| |
| what_am_i_doing = NO_DATA_STAGE; |
| |
| /* Expect a zero-length IN for the Status phase */ |
| (void) load_in_fifo(0, 0); |
| |
| /* We apparently have to do this every time we transmit anything */ |
| flush_in_fifo(); |
| |
| /* I don't think we have to do this every time, but the Programmer's |
| * Guide says to, so... */ |
| GR_USB_DIEPDMA(0) = (uint32_t)(cur_in_desc); |
| |
| /* Blindly following instructions here, too. */ |
| if (tc == TABLE_CASE_C) |
| GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; |
| else |
| GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; |
| |
| /* The Programmer's Guide instructions for the Normal Two-Stage Control |
| * Transfer leave this next bit out, so we only need it if we intend to |
| * process an Exceptional Two-Stage Control Transfer. Because obviously |
| * we always know in advance what the host is going to do. Idiots. */ |
| |
| /* Be prepared to get a new Setup packet during the Status phase */ |
| next_out_desc->flags = |
| DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
| | DOEPDMA_IOC | DOEPDMA_LAST; |
| |
| /* We've already set GR_USB_DOEPDMA(0), so just enable it. */ |
| if (tc == TABLE_CASE_C) |
| GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; |
| else |
| GR_USB_DOEPCTL(0) = DXEPCTL_EPENA; |
| |
| /* Get an interrupt when either IN or OUT arrives */ |
| GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); |
| } |
| |
| /* Handle a Setup packet that expects us to send back data in reply. Return the |
| * length of the data we're returning, or negative to indicate an error. */ |
| static int handle_setup_with_in_stage(enum table_case tc, |
| struct usb_setup_packet *req) |
| { |
| const void *data = 0; |
| uint32_t len = 0; |
| int ugly_hack = 0; |
| static const uint16_t zero; /* == 0 */ |
| |
| print_later("handle_setup_with_in_stage(%c)", "0ABCDE67"[tc], |
| 0, 0, 0, 0); |
| |
| switch (req->bRequest) { |
| case USB_REQ_GET_DESCRIPTOR: { |
| uint8_t type = req->wValue >> 8; |
| uint8_t idx = req->wValue & 0xff; |
| |
| switch (type) { |
| case USB_DT_DEVICE: |
| data = &dev_desc; |
| len = sizeof(dev_desc); |
| break; |
| case USB_DT_CONFIGURATION: |
| data = __usb_desc; |
| len = USB_DESC_SIZE; |
| ugly_hack = 1; /* see below */ |
| break; |
| #ifdef CONFIG_USB_BOS |
| case USB_DT_BOS: |
| data = bos_ctx.descp; |
| len = bos_ctx.size; |
| break; |
| #endif |
| case USB_DT_STRING: |
| if (idx >= USB_STR_COUNT) |
| return -1; |
| #ifdef CONFIG_USB_SERIALNO |
| if (idx == USB_STR_SERIALNO && ccd_ext_is_enabled()) |
| data = usb_serialno_desc; |
| else |
| #endif |
| data = usb_strings[idx]; |
| len = *(uint8_t *)data; |
| break; |
| case USB_DT_DEVICE_QUALIFIER: |
| /* We're not high speed */ |
| return -1; |
| case USB_DT_DEBUG: |
| return -1; |
| default: |
| report_error(type); |
| return -1; |
| } |
| break; |
| } |
| case USB_REQ_GET_STATUS: { |
| /* TODO: Device Status: Remote Wakeup? Self Powered? */ |
| data = &zero; |
| len = sizeof(zero); |
| break; |
| } |
| case USB_REQ_GET_CONFIGURATION: |
| data = &configuration_value; |
| len = sizeof(configuration_value); |
| break; |
| |
| case USB_REQ_SYNCH_FRAME: |
| /* Unimplemented */ |
| return -1; |
| |
| default: |
| report_error(req->bRequest); |
| return -1; |
| } |
| |
| /* Don't send back more than we were asked for. */ |
| len = MIN(req->wLength, len); |
| |
| /* Prepare the TX FIFO. If we haven't preallocated enough room in the |
| * TX FIFO for the largest reply, we'll have to stall. This is a bug in |
| * our code, but detecting it easily at compile time is related to the |
| * ugly_hack directly below. */ |
| if (load_in_fifo(data, len) < 0) |
| return -1; |
| |
| if (ugly_hack) { |
| /* |
| * TODO: Somebody figure out how to fix this, please. |
| * |
| * The USB configuration descriptor request is unique in that |
| * it not only returns the configuration descriptor, but also |
| * all the interface descriptors and all their endpoint |
| * descriptors as one enormous blob. We've set up some macros |
| * so we can declare and implement separate interfaces in |
| * separate files just by compiling them, and all the relevant |
| * descriptors are sorted and bundled up by the linker. But the |
| * total length of the entire blob needs to appear in the first |
| * configuration descriptor struct and because we don't know |
| * that value until after linking, it can't be initialized as a |
| * constant. So we have to compute it at run-time and shove it |
| * in here, which also means that we have to copy the whole |
| * blob into our TX FIFO buffer so that it's mutable. Otherwise |
| * we could just point at it (or pretty much any other constant |
| * struct that we wanted to send to the host). Bah. |
| */ |
| struct usb_config_descriptor *cfg = |
| (struct usb_config_descriptor *)ep0_in_buf; |
| /* set the real descriptor size */ |
| cfg->wTotalLength = USB_DESC_SIZE; |
| } |
| |
| return len; |
| } |
| |
| /* Handle a Setup that comes with additional data for us. */ |
| static int handle_setup_with_out_stage(enum table_case tc, |
| struct usb_setup_packet *req) |
| { |
| print_later("handle_setup_with_out_stage(%c)", "0ABCDE67"[tc], |
| 0, 0, 0, 0); |
| |
| /* TODO: We don't support any of these. We should. */ |
| return -1; |
| } |
| |
| /* Some Setup packets don't have a data stage at all. */ |
| static int handle_setup_with_no_data_stage(enum table_case tc, |
| struct usb_setup_packet *req) |
| { |
| uint8_t set_addr; |
| |
| print_later("handle_setup_with_no_data_stage(%c)", "0ABCDE67"[tc], |
| 0, 0, 0, 0); |
| |
| switch (req->bRequest) { |
| case USB_REQ_SET_ADDRESS: |
| /* |
| * Set the address after the IN packet handshake. |
| * |
| * From the USB 2.0 spec, section 9.4.6: |
| * |
| * As noted elsewhere, requests actually may result in |
| * up to three stages. In the first stage, the Setup |
| * packet is sent to the device. In the optional second |
| * stage, data is transferred between the host and the |
| * device. In the final stage, status is transferred |
| * between the host and the device. The direction of |
| * data and status transfer depends on whether the host |
| * is sending data to the device or the device is |
| * sending data to the host. The Status stage transfer |
| * is always in the opposite direction of the Data |
| * stage. If there is no Data stage, the Status stage |
| * is from the device to the host. |
| * |
| * Stages after the initial Setup packet assume the |
| * same device address as the Setup packet. The USB |
| * device does not change its device address until |
| * after the Status stage of this request is completed |
| * successfully. Note that this is a difference between |
| * this request and all other requests. For all other |
| * requests, the operation indicated must be completed |
| * before the Status stage |
| */ |
| set_addr = req->wValue & 0xff; |
| /* |
| * NOTE: Now that we've said that, we don't do it. The |
| * hardware for this SoC knows that an IN packet will |
| * be following the SET ADDRESS, so it waits until it |
| * sees that happen before the address change takes |
| * effect. If we wait until after the IN packet to |
| * change the register, the hardware gets confused and |
| * doesn't respond to anything. |
| */ |
| GWRITE_FIELD(USB, DCFG, DEVADDR, set_addr); |
| CPRINTS("SETAD 0x%02x (%d)", set_addr, set_addr); |
| print_later("SETAD 0x%02x (%d)", set_addr, set_addr, 0, 0, 0); |
| device_state = DS_ADDRESS; |
| processed_update_counter = 1; |
| break; |
| |
| case USB_REQ_SET_CONFIGURATION: |
| print_later("SETCFG 0x%x", req->wValue, 0, 0, 0, 0); |
| switch (req->wValue) { |
| case 0: |
| configuration_value = req->wValue; |
| device_state = DS_ADDRESS; |
| break; |
| case 1: /* Caution: Only one config descriptor TODAY */ |
| /* TODO: All endpoints set to DATA0 toggle state */ |
| configuration_value = req->wValue; |
| device_state = DS_CONFIGURED; |
| break; |
| default: |
| /* Nope. That's a paddlin. */ |
| return -1; |
| } |
| break; |
| |
| case USB_REQ_CLEAR_FEATURE: |
| case USB_REQ_SET_FEATURE: |
| /* TODO: Handle DEVICE_REMOTE_WAKEUP, ENDPOINT_HALT? */ |
| print_later("SET_FEATURE/CLEAR_FEATURE. Whatever...", |
| 0, 0, 0, 0, 0); |
| break; |
| |
| default: |
| /* Anything else is unsupported */ |
| return -1; |
| } |
| |
| /* No data to transfer, go straight to the Status phase. */ |
| return 0; |
| } |
| |
| /* Dispatch an incoming Setup packet according to its type */ |
| static void handle_setup(enum table_case tc) |
| { |
| struct usb_setup_packet *req = cur_out_desc->addr; |
| int data_phase_in = req->bmRequestType & USB_DIR_IN; |
| int data_phase_out = !data_phase_in && req->wLength; |
| int bytes = -1; /* default is to stall */ |
| |
| print_later("R: %02x %02x %04x %04x %04x", |
| req->bmRequestType, req->bRequest, |
| req->wValue, req->wIndex, req->wLength); |
| |
| if (0 == (req->bmRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))) { |
| /* Standard Device requests */ |
| if (data_phase_in) |
| bytes = handle_setup_with_in_stage(tc, req); |
| else if (data_phase_out) |
| bytes = handle_setup_with_out_stage(tc, req); |
| else |
| bytes = handle_setup_with_no_data_stage(tc, req); |
| } else if (USB_RECIP_INTERFACE == |
| (req->bmRequestType & USB_RECIP_MASK)) { |
| /* Interface-specific requests */ |
| uint8_t iface = req->wIndex & 0xff; |
| |
| print_later("iface %d request (vs %d)", |
| iface, USB_IFACE_COUNT, 0, 0, 0); |
| if (iface < USB_IFACE_COUNT) { |
| bytes = usb_iface_request[iface](req); |
| print_later(" iface returned %d", bytes, 0, 0, 0, 0); |
| } |
| } else { |
| #ifdef CONFIG_WEBUSB_URL |
| if (data_phase_in && |
| ((req->bmRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR)) { |
| if (req->bRequest == 0x01 && |
| req->wIndex == WEBUSB_REQ_GET_URL) { |
| bytes = *(uint8_t *)webusb_url; |
| bytes = MIN(req->wLength, bytes); |
| if (load_in_fifo(webusb_url, bytes) < 0) |
| bytes = -1; |
| } else { |
| report_error(-1); |
| } |
| } else |
| #endif |
| /* Something we need to add support for? */ |
| report_error(-1); |
| } |
| |
| print_later("data_phase_in %d data_phase_out %d bytes %d", |
| !!data_phase_in, !!data_phase_out, bytes, 0, 0); |
| |
| /* We say "no" to unsupported and intentionally unhandled requests by |
| * stalling the Data and/or Status stage. */ |
| if (bytes < 0) { |
| /* Stall both IN and OUT. SETUP will come through anyway. */ |
| stall_both_fifos(); |
| } else { |
| if (data_phase_in) |
| expect_data_phase_in(tc); |
| else if (data_phase_out) |
| expect_data_phase_out(tc); |
| else |
| expect_status_phase_in(tc); |
| } |
| } |
| |
| /* 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; |
| } |
| } |
| |
| /****************************************************************************/ |
| /* USB device initialization and shutdown routines */ |
| |
| /* |
| * DATA FIFO Setup. There is an internal SPRAM used to buffer the IN/OUT |
| * packets and track related state without hammering the AHB and system RAM |
| * during USB transactions. We have to specify where and how much of that SPRAM |
| * to use for what. |
| * |
| * See Programmer's Guide chapter 2, "Calculating FIFO Size". |
| * We're using Dedicated TxFIFO Operation, without enabling thresholding. |
| * |
| * Section 2.1.1.2, page 30: RXFIFO size is the same as for Shared FIFO, which |
| * is Section 2.1.1.1, page 28. This is also the same as Method 2 on page 45. |
| * |
| * We support up to 3 control EPs, no periodic IN EPs, up to 16 TX EPs. Max |
| * data packet size is 64 bytes. Total SPRAM available is 1024 slots. |
| */ |
| #define MAX_CONTROL_EPS 3 |
| #define MAX_NORMAL_EPS 16 |
| #define FIFO_RAM_DEPTH 1024 |
| /* |
| * Device RX FIFO size is thus: |
| * (4 * 3 + 6) + 2 * ((64 / 4) + 1) + (2 * 16) + 1 == 85 |
| */ |
| #define RXFIFO_SIZE ((4 * MAX_CONTROL_EPS + 6) + \ |
| 2 * ((USB_MAX_PACKET_SIZE / 4) + 1) + \ |
| (2 * MAX_NORMAL_EPS) + 1) |
| /* |
| * Device TX FIFO size is 2 * (64 / 4) == 32 for each IN EP (Page 46). |
| */ |
| #define TXFIFO_SIZE (2 * (USB_MAX_PACKET_SIZE / 4)) |
| /* |
| * We need 4 slots per endpoint direction for endpoint status stuff (Table 2-1, |
| * unconfigurable). |
| */ |
| #define EP_STATUS_SIZE (4 * MAX_NORMAL_EPS * 2) |
| /* |
| * Make sure all that fits. |
| */ |
| BUILD_ASSERT(RXFIFO_SIZE + TXFIFO_SIZE * MAX_NORMAL_EPS + EP_STATUS_SIZE < |
| FIFO_RAM_DEPTH); |
| |
| /* Now put those constants into the correct registers */ |
| static void setup_data_fifos(void) |
| { |
| int i; |
| |
| print_later("setup_data_fifos()", 0, 0, 0, 0, 0); |
| |
| /* Programmer's Guide, p31 */ |
| GR_USB_GRXFSIZ = RXFIFO_SIZE; /* RXFIFO */ |
| GR_USB_GNPTXFSIZ = (TXFIFO_SIZE << 16) | RXFIFO_SIZE; /* TXFIFO 0 */ |
| |
| /* TXFIFO 1..15 */ |
| for (i = 1; i < MAX_NORMAL_EPS; i++) |
| GR_USB_DIEPTXF(i) = ((TXFIFO_SIZE << 16) | |
| (RXFIFO_SIZE + i * TXFIFO_SIZE)); |
| |
| /* |
| * TODO: The Programmer's Guide is confusing about when or whether to |
| * flush the FIFOs. Section 2.1.1.2 (p31) just says to flush. Section |
| * 2.2.2 (p55) says to stop all the FIFOs first, then flush. Section |
| * 7.5.4 (p162) says that flushing the RXFIFO at reset is not |
| * recommended at all. |
| * |
| * I'm also unclear on whether or not the individual EPs are expected |
| * to be disabled already (DIEPCTLn/DOEPCTLn.EPENA == 0), and if so, |
| * whether by firmware or hardware. |
| */ |
| |
| /* Flush all FIFOs according to Section 2.1.1.2 */ |
| GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH |
| | GRSTCTL_RXFFLSH; |
| while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) |
| ; /* TODO: timeout 100ms */ |
| } |
| |
| static void usb_init_endpoints(void) |
| { |
| int ep; |
| |
| print_later("usb_init_endpoints()", 0, 0, 0, 0, 0); |
| |
| /* Prepare to receive packets on EP0 */ |
| initialize_dma_buffers(); |
| expect_setup_packet(); |
| |
| /* Reset the other endpoints */ |
| for (ep = 1; ep < USB_EP_COUNT; ep++) |
| usb_ep_reset[ep](); |
| } |
| |
| static void usb_reset(void) |
| { |
| CPRINTS("%s, status %x", __func__, GR_USB_GINTSTS); |
| print_later("usb_reset()", 0, 0, 0, 0, 0); |
| |
| /* Clear our internal state */ |
| device_state = DS_DEFAULT; |
| configuration_value = 0; |
| |
| /* Clear the device address */ |
| GWRITE_FIELD(USB, DCFG, DEVADDR, 0); |
| |
| /* Reinitialize all the endpoints */ |
| usb_init_endpoints(); |
| } |
| |
| void usb_interrupt(void) |
| { |
| uint32_t status = GR_USB_GINTSTS; |
| uint32_t oepint = status & GINTSTS(OEPINT); |
| uint32_t iepint = status & GINTSTS(IEPINT); |
| |
| int ep; |
| |
| print_later("interrupt: GINTSTS 0x%08x", status, 0, 0, 0, 0); |
| |
| /* We can suspend if the host stops talking to us. But if anything else |
| * comes along (even ERLYSUSP), we should NOT suspend. */ |
| if (status & GINTSTS(USBSUSP)) { |
| print_later("usb_suspend()", 0, 0, 0, 0, 0); |
| enable_sleep(SLEEP_MASK_USB_DEVICE); |
| } else { |
| disable_sleep(SLEEP_MASK_USB_DEVICE); |
| } |
| |
| #ifdef DEBUG_ME |
| if (status & GINTSTS(ERLYSUSP)) |
| print_later("usb_early_suspend()", 0, 0, 0, 0, 0); |
| |
| if (status & GINTSTS(WKUPINT)) |
| print_later("usb_wakeup()", 0, 0, 0, 0, 0); |
| |
| if (status & GINTSTS(ENUMDONE)) |
| print_later("usb_enumdone()", 0, 0, 0, 0, 0); |
| #endif |
| |
| if (status & (GINTSTS(RESETDET) | GINTSTS(USBRST))) |
| usb_reset(); |
| |
| /* Initialize the SOF clock calibrator only on the first SOF */ |
| if (GR_USB_GINTMSK & GINTMSK(SOF) && status & GINTSTS(SOF)) { |
| init_sof_clock(); |
| GR_USB_GINTMSK &= ~GINTMSK(SOF); |
| } |
| |
| /* Endpoint interrupts */ |
| if (oepint || iepint) { |
| /* Note: It seems that the DAINT bits are only trustworthy for |
| * identifying interrupts when selected by the corresponding |
| * OEPINT and IEPINT bits from GINTSTS. */ |
| uint32_t daint = GR_USB_DAINT; |
| |
| print_later(" oepint%c iepint%c daint 0x%08x", |
| oepint ? '!' : '_', iepint ? '!' : '_', |
| daint, 0, 0); |
| |
| /* EP0 has a combined IN/OUT handler. Only call it once, but |
| * let it know which direction(s) had an interrupt. */ |
| if (daint & (DAINT_OUTEP(0) | DAINT_INEP(0))) { |
| uint32_t intr_on_out = (oepint && |
| (daint & DAINT_OUTEP(0))); |
| uint32_t intr_on_in = (iepint && |
| (daint & DAINT_INEP(0))); |
| ep0_interrupt(intr_on_out, intr_on_in); |
| } |
| |
| /* Invoke the unidirectional IN and OUT functions for the other |
| * endpoints. Each handler must clear their own bits in |
| * DIEPINTn/DOEPINTn. */ |
| for (ep = 1; ep < USB_EP_COUNT; ep++) { |
| if (oepint && (daint & DAINT_OUTEP(ep))) |
| usb_ep_rx[ep](); |
| if (iepint && (daint & DAINT_INEP(ep))) |
| usb_ep_tx[ep](); |
| } |
| } |
| |
| if (status & GINTSTS(GOUTNAKEFF)) |
| GR_USB_DCTL |= DCTL_CGOUTNAK; |
| |
| if (status & GINTSTS(GINNAKEFF)) |
| GR_USB_DCTL |= DCTL_CGNPINNAK; |
| |
| GR_USB_GINTSTS = status; |
| |
| print_later("end of interrupt", 0, 0, 0, 0, 0); |
| } |
| DECLARE_IRQ(GC_IRQNUM_USB0_USBINTR, usb_interrupt, 1); |
| |
| static void usb_softreset(void) |
| { |
| int timeout; |
| |
| GR_USB_GRSTCTL = GRSTCTL_CSFTRST; |
| timeout = 10000; |
| while ((GR_USB_GRSTCTL & GRSTCTL_CSFTRST) && timeout-- > 0) |
| ; |
| if (GR_USB_GRSTCTL & GRSTCTL_CSFTRST) { |
| CPRINTF("USB: reset failed\n"); |
| return; |
| } |
| |
| timeout = 10000; |
| while (!(GR_USB_GRSTCTL & GRSTCTL_AHBIDLE) && timeout-- > 0) |
| ; |
| if (!timeout) { |
| CPRINTF("USB: reset timeout\n"); |
| return; |
| } |
| /* TODO: Wait 3 PHY clocks before returning */ |
| |
| #ifdef BOARD_CR50 |
| /* |
| * TODO(b/63867566): This delay is added to get usb to suspend after |
| * resume from deep sleep. Find out what the root cause is and add a |
| * fix. |
| */ |
| usleep(100); |
| #endif |
| } |
| |
| void usb_connect(void) |
| { |
| print_later("usb_connect()", 0, 0, 0, 0, 0); |
| GR_USB_DCTL &= ~DCTL_SFTDISCON; |
| } |
| |
| void usb_disconnect(void) |
| { |
| print_later("usb_disconnect()", 0, 0, 0, 0, 0); |
| GR_USB_DCTL |= DCTL_SFTDISCON; |
| |
| device_state = DS_DEFAULT; |
| configuration_value = 0; |
| } |
| |
| void usb_save_suspended_state(void) |
| { |
| int i; |
| uint32_t pid = 0; |
| |
| /* Record the state the DATA PIDs toggling on each endpoint. */ |
| for (i = 1; i < USB_EP_COUNT; i++) { |
| if (GR_USB_DOEPCTL(i) & DXEPCTL_DPID) |
| pid |= (1 << i); |
| if (GR_USB_DIEPCTL(i) & DXEPCTL_DPID) |
| pid |= (1 << (i + 16)); |
| } |
| /* Save the USB device address */ |
| GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG; |
| GREG32(PMU, PWRDN_SCRATCH19) = pid; |
| |
| } |
| |
| void usb_restore_suspended_state(void) |
| { |
| int i; |
| uint32_t pid; |
| |
| /* restore the USB device address (the DEVADDR field). */ |
| GR_USB_DCFG = GREG32(PMU, PWRDN_SCRATCH18); |
| /* Restore the DATA PIDs on endpoints. */ |
| pid = GREG32(PMU, PWRDN_SCRATCH19); |
| for (i = 1; i < USB_EP_COUNT; i++) { |
| GR_USB_DOEPCTL(i) = pid & (1 << i) ? |
| DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID; |
| GR_USB_DIEPCTL(i) = pid & (1 << (i + 16)) ? |
| DXEPCTL_SET_D1PID : DXEPCTL_SET_D0PID; |
| } |
| } |
| |
| 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; |
| |
| /* Disable the PHY clock whenever usb suspend is detected */ |
| GWRITE_FIELD(USB, PCGCCTL, STOPPCLK, 1); |
| |
| /* 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) |
| usb_restore_suspended_state(); |
| 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 and usb wakeup */ |
| GINTMSK(SOF) | GINTMSK(WKUPINT); |
| |
| /* 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 |
| } |
| #ifndef CONFIG_USB_INHIBIT_INIT |
| DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT - 2); |
| #endif |
| |
| void usb_release(void) |
| { |
| /* signal disconnect to host */ |
| usb_disconnect(); |
| |
| /* disable interrupt handlers */ |
| task_disable_irq(GC_IRQNUM_USB0_USBINTR); |
| |
| /* Deactivate the PHY */ |
| GR_USB_GGPIO = GGPIO_WRITE(USB_CUSTOM_CFG_REG, 0); |
| |
| /* disable clocks */ |
| clock_enable_module(MODULE_USB, 0); |
| /* TODO: pin-mux */ |
| |
| /* USB is off, so sleep whenever */ |
| enable_sleep(SLEEP_MASK_USB_DEVICE); |
| } |
| |
| static int command_usb(int argc, char **argv) |
| { |
| int val; |
| |
| if (argc > 1) { |
| if (parse_bool(argv[1], &val)) { |
| if (val) |
| usb_init(); |
| else |
| usb_release(); |
| #ifdef CONFIG_USB_SELECT_PHY |
| } else if (!strcasecmp("a", argv[1])) { |
| usb_select_phy(USB_SEL_PHY0); |
| } else if (!strcasecmp("b", argv[1])) { |
| usb_select_phy(USB_SEL_PHY1); |
| #endif |
| } else |
| return EC_ERROR_PARAM1; |
| } |
| |
| showregs(); |
| ccprintf("PHY %c\n", (which_phy == USB_SEL_PHY0 ? 'A' : 'B')); |
| |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(usb, command_usb, |
| #ifdef CONFIG_USB_SELECT_PHY |
| "[<BOOLEAN> | a | b]", |
| #else |
| "<BOOLEAN>", |
| #endif |
| "Get/set the USB connection state and PHY selection"); |
| |
| #ifdef CONFIG_USB_SERIALNO |
| /* This will be subbed into USB_STR_SERIALNO. */ |
| struct usb_string_desc *usb_serialno_desc = |
| USB_WR_STRING_DESC(DEFAULT_SERIALNO); |
| |
| /* Update serial number */ |
| static int usb_set_serial(const char *serialno) |
| { |
| struct usb_string_desc *sd = usb_serialno_desc; |
| int i; |
| |
| if (!serialno) |
| return EC_ERROR_INVAL; |
| |
| /* Convert into unicode usb string desc. */ |
| for (i = 0; i < USB_STRING_LEN; i++) { |
| sd->_data[i] = serialno[i]; |
| if (serialno[i] == 0) |
| break; |
| } |
| /* Count wchars (w/o null terminator) plus size & type bytes. */ |
| sd->_len = (i * 2) + 2; |
| sd->_type = USB_DT_STRING; |
| |
| return EC_SUCCESS; |
| } |
| |
| static void usb_load_serialno(void) |
| { |
| char devid_str[20]; |
| |
| snprintf(devid_str, 20, "%08X-%08X", GREG32(FUSE, DEV_ID0), |
| GREG32(FUSE, DEV_ID1)); |
| |
| usb_set_serial(devid_str); |
| } |
| DECLARE_HOOK(HOOK_INIT, usb_load_serialno, HOOK_PRIO_DEFAULT - 1); |
| |
| static int command_serialno(int argc, char **argv) |
| { |
| struct usb_string_desc *sd = usb_serialno_desc; |
| char buf[USB_STRING_LEN]; |
| int rv = EC_SUCCESS; |
| int i; |
| |
| if (argc != 1) { |
| ccprintf("Setting serial number\n"); |
| rv = usb_set_serial(argv[1]); |
| } |
| |
| for (i = 0; i < USB_STRING_LEN; i++) |
| buf[i] = sd->_data[i]; |
| ccprintf("Serial number: %s\n", buf); |
| return rv; |
| } |
| |
| DECLARE_CONSOLE_COMMAND(serialno, command_serialno, |
| "[value]", |
| "Read and write USB serial number"); |
| #endif |