summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2015-10-19 15:44:25 -0700
committerchrome-bot <chrome-bot@chromium.org>2015-10-22 22:36:03 -0700
commitd8b81cdc0f0e98af3625c082d0b3ee3f22944ee7 (patch)
treed448b707bc97e8c3863c360cb6ff2b2c24ac5b14 /chip
parent061abcea96d7ca12b75e62bc987d7496392c0f8b (diff)
downloadchrome-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.h6
-rw-r--r--chip/g/usb.c159
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);