diff options
author | Bill Richardson <wfrichar@chromium.org> | 2015-10-19 15:44:25 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2015-10-22 22:36:03 -0700 |
commit | d8b81cdc0f0e98af3625c082d0b3ee3f22944ee7 (patch) | |
tree | d448b707bc97e8c3863c360cb6ff2b2c24ac5b14 /chip | |
parent | 061abcea96d7ca12b75e62bc987d7496392c0f8b (diff) | |
download | chrome-ec-d8b81cdc0f0e98af3625c082d0b3ee3f22944ee7.tar.gz |
Cr50: Support USB on 15MHz FPGA image
The latest Cr50 FPGA release runs at 15MHz, but supports USB
operations. This CL includes changes to make that work.
Specifically:
* Enable the security features and select the correct PHY
* Adjust the turnaround time for the slower clock speed
* Handle the SET ADDRESS command specially for this SoC
* Remove all printfs from interrupt handlers (but add #ifdef code
to print debug messages later if desired).
BUG=chrome-os-partner:34893
BRANCH=none
TEST=make buildall, manual test of Cr50 USB:
1. Plug into a USB jack on a Linux host.
2. In src/platform/ec/extra/usb_console, run
make
./usb_console -p 5014 -e 1
3. Type something, hit return
4. See whatever you typed come back with swapped case
5. ^D to quit
Change-Id: I848e96d19df056a453d30d4b5537481046fe852d
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/308062
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/g/registers.h | 6 | ||||
-rw-r--r-- | chip/g/usb.c | 159 |
2 files changed, 152 insertions, 13 deletions
diff --git a/chip/g/registers.h b/chip/g/registers.h index 2fb807d740..b1e6d2fe09 100644 --- a/chip/g/registers.h +++ b/chip/g/registers.h @@ -361,6 +361,7 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer, #define GR_USB_GRXSTSP GR_USB_REG(GC_USB_GRXSTSP_OFFSET) #define GR_USB_GRXFSIZ GR_USB_REG(GC_USB_GRXFSIZ_OFFSET) #define GR_USB_GNPTXFSIZ GR_USB_REG(GC_USB_GNPTXFSIZ_OFFSET) +#define GR_USB_GGPIO GR_USB_REG(GC_USB_GGPIO_OFFSET) #define GR_USB_GSNPSID GR_USB_REG(GC_USB_GSNPSID_OFFSET) #define GR_USB_GHWCFG1 GR_USB_REG(GC_USB_GHWCFG1_OFFSET) #define GR_USB_GHWCFG2 GR_USB_REG(GC_USB_GHWCFG2_OFFSET) @@ -397,7 +398,10 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer, #define GAHBCFG_HBSTLEN_INCR4 (3 << GC_USB_GAHBCFG_HBSTLEN_LSB) #define GAHBCFG_NP_TXF_EMP_LVL (1 << GC_USB_GAHBCFG_NPTXFEMPLVL_LSB) -#define GUSBCFG_TOUTCAL(n) (((n) << GC_USB_GUSBCFG_TOUTCAL_LSB) & GC_USB_GUSBCFG_TOUTCAL_MASK) +#define GUSBCFG_TOUTCAL(n) (((n) << GC_USB_GUSBCFG_TOUTCAL_LSB) \ + & GC_USB_GUSBCFG_TOUTCAL_MASK) +#define GUSBCFG_USBTRDTIM(n) (((n) << GC_USB_GUSBCFG_USBTRDTIM_LSB) \ + & GC_USB_GUSBCFG_USBTRDTIM_MASK) #define GUSBCFG_PHYSEL_HS (0 << GC_USB_GUSBCFG_PHYSEL_LSB) #define GUSBCFG_PHYSEL_FS (1 << GC_USB_GUSBCFG_PHYSEL_LSB) #define GUSBCFG_FSINTF_6PIN (0 << GC_USB_GUSBCFG_FSINTF_LSB) diff --git a/chip/g/usb.c b/chip/g/usb.c index f892d65fdb..2cd1df1e4c 100644 --- a/chip/g/usb.c +++ b/chip/g/usb.c @@ -15,6 +15,7 @@ #include "timer.h" #include "util.h" #include "usb.h" +#include "watchdog.h" /* Rev A1 has a RTL bug in the FIFO */ #if CONCAT2(GC_, GC___MAJOR_REV__) == GC___REVA__ @@ -40,6 +41,69 @@ #define CPRINTS(format, args...) cprints(CC_USB, format, ## args) #define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) +/* + * 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. Use this instead to print when we have a chance. + */ +#if 1 +/* do nothing */ +#define print_later(...) +#else +/* + * Fill a circular buffer with things to print when we get the chance. The + * number of args is fixed, and there's no rollover detection. + */ +#define MAX_ENTRIES 256 +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; + +/* Call this only from within interrupt handler! */ +void print_later(const char *fmt, int a0, int a1, int a2, int a3, int a4) +{ + 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].a3 = a4; + stuff_in++; + if (stuff_in >= MAX_ENTRIES) + stuff_in = 0; +} + +static void do_print_later(void) +{ + int lines_per_loop = 32; /* too much at once fails */ + int copy_of_stuff_in; + + interrupt_disable(); + copy_of_stuff_in = stuff_in; + interrupt_enable(); + + 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++; + if (stuff_out >= MAX_ENTRIES) + stuff_out = 0; + lines_per_loop--; + } +} +DECLARE_HOOK(HOOK_TICK, do_print_later, HOOK_PRIO_DEFAULT); +#endif + #ifdef CONFIG_USB_BOS /* v2.01 (vs 2.00) BOS Descriptor provided */ #define USB_DEV_BCDUSB 0x0201 @@ -116,8 +180,14 @@ static void ep0_rx(void) uint32_t epint = GR_USB_DOEPINT(0); struct usb_setup_packet *req = (struct usb_setup_packet *)ep0_buf_rx; + print_later("ep0_rx: DOEPINT(0) is 0x%x", epint, 0, 0, 0, 0); + GR_USB_DOEPINT(0) = epint; /* clear IT */ + print_later("R: %02x %02x %04x %04x %04x", + req->bmRequestType, req->bRequest, req->wValue, + req->wIndex, req->wLength); + /* reset any incomplete descriptor transfer */ desc_ptr = NULL; @@ -185,6 +255,7 @@ static void ep0_rx(void) } ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC | DIEPDMA_TXBYTES(len); + GR_USB_DIEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA; ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; @@ -204,11 +275,50 @@ static void ep0_rx(void) } else if (req->bmRequestType == USB_DIR_OUT) { switch (req->bRequest) { case USB_REQ_SET_ADDRESS: - /* set the address after we got IN packet handshake */ + /* + * 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; - /* need null IN transaction -> TX Valid */ - ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC | - DIEPDMA_TXBYTES(0) | DIEPDMA_SP; + /* + * 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 make + * the change, the hardware gets confused and doesn't + * respond to anything. + */ + GR_USB_DCFG = (GR_USB_DCFG & ~DCFG_DEVADDR(0x7f)) + | DCFG_DEVADDR(set_addr); + print_later("SETAD 0x%02x (%d)", + set_addr, set_addr, 0, 0, 0); + /* still need a null IN transaction -> TX Valid */ + ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY + | DIEPDMA_IOC | DIEPDMA_TXBYTES(0) | DIEPDMA_SP; GR_USB_DIEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA; ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; @@ -216,6 +326,7 @@ static void ep0_rx(void) break; case USB_REQ_SET_CONFIGURATION: /* uint8_t cfg = req->wValue & 0xff; */ + print_later("SETCFG 0x%x", req->wValue, 0, 0, 0, 0); /* null IN for handshake */ ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC | DIEPDMA_TXBYTES(0) | DIEPDMA_SP; @@ -234,6 +345,7 @@ static void ep0_rx(void) return; unknown_req: + print_later("unknown req", 0, 0, 0, 0, 0); ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA; @@ -247,10 +359,11 @@ static void ep0_tx(void) GR_USB_DIEPINT(0) = epint; /* clear IT */ + /* This is where most SoCs would change the address. + * We don't. See the note above. */ if (set_addr) { - GR_USB_DCFG = (GR_USB_DCFG & ~DCFG_DEVADDR(0x7f)) - | DCFG_DEVADDR(set_addr); - CPRINTS("SETAD 0x%02x (%d)", set_addr, set_addr); + print_later("STATUS SETAD 0x%02x (%d)", + set_addr, set_addr, 0, 0, 0); set_addr = 0; } if (desc_ptr) { @@ -270,6 +383,8 @@ static void ep0_tx(void) static void ep0_reset(void) { + print_later("X", 0, 0, 0, 0, 0); + ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; ep0_out_desc.addr = ep0_buf_rx; @@ -334,6 +449,7 @@ static void usb_softreset(void) { int timeout; + GR_USB_GGPIO = 0x80400000; GR_USB_GRSTCTL = GRSTCTL_CSFTRST; timeout = 10000; while ((GR_USB_GRSTCTL & GRSTCTL_CSFTRST) && timeout-- > 0) @@ -354,6 +470,7 @@ static void usb_softreset(void) void usb_connect(void) { + GR_USB_GGPIO = 0x80400000; GR_USB_DCTL &= ~DCTL_SFTDISCON; } @@ -365,6 +482,18 @@ void usb_disconnect(void) void usb_init(void) { int i; + + /* TODO(wfrichar): Clean this up. Do only what's needed, and use + * meaningful constants 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); @@ -375,15 +504,21 @@ void usb_init(void) GR_USB_GDFIFOCFG = ((FIFO_SIZE - 0x80) << 16) | FIFO_SIZE; /* PHY configuration */ + GR_USB_GGPIO = 0x80400000; + /* Full-Speed Serial PHY */ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN - | GUSBCFG_TOUTCAL(7) | (9 << 10); + | GUSBCFG_TOUTCAL(7) + /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ + | GUSBCFG_USBTRDTIM(14); + usb_softreset(); - /* PHY configuration */ - /* Full-Speed Serial PHY */ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN - | GUSBCFG_TOUTCAL(7) | (9 << 10); + | GUSBCFG_TOUTCAL(7) + /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ + | GUSBCFG_USBTRDTIM(14); + /* Global + DMA configuration */ GR_USB_GAHBCFG = GAHBCFG_DMA_EN | GAHBCFG_GLB_INTR_EN | GAHBCFG_NP_TXF_EMP_LVL; @@ -446,7 +581,7 @@ void usb_init(void) usb_connect(); #endif - CPRINTS("USB init done"); + print_later("USB init done", 0, 0, 0, 0, 0); } #ifndef CONFIG_USB_INHIBIT_INIT DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT); |