summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-11-19 14:37:10 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-04-02 20:27:46 +0000
commit4d0aad889476e3012f970fffa5305d5c9386cc47 (patch)
treeb9cf28b3a7b5ca1695c07d5451b4f170626e6136
parentde24d511621fcc98b36152a7c373eeaa566fea74 (diff)
downloadchrome-ec-4d0aad889476e3012f970fffa5305d5c9386cc47.tar.gz
cr50: add USB support
Add a USB device driver for the Synopsys DWC USB device controller. The common USB protocol stack code still need to be de-duplicated with the STM32 implementation. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:33919 TEST=plug Cr50 to a Linux workstation and see USB descriptors using "lsusb -v -d 18d1:5014" Change-Id: I4a367241053de2c2d94aa06f82ea4bee51f9f89a Reviewed-on: https://chromium-review.googlesource.com/231160 Commit-Queue: Vincent Palatin <vpalatin@chromium.org> Trybot-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--board/cr50/board.c33
-rw-r--r--board/cr50/board.h29
-rw-r--r--chip/g/build.mk4
-rw-r--r--chip/g/config_chip.h7
-rw-r--r--chip/g/registers.h146
-rw-r--r--chip/g/usb.c461
-rw-r--r--chip/g/usb_console.c266
-rw-r--r--chip/g/usb_endpoints.S107
-rw-r--r--chip/g/usb_hid.c170
9 files changed, 1223 insertions, 0 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c
index 92a6808c11..3b7e1613d9 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -5,10 +5,13 @@
#include "common.h"
#include "console.h"
+#include "ec_version.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
#include "task.h"
+#include "usb.h"
+#include "usb_hid.h"
#include "util.h"
/*
@@ -21,6 +24,26 @@
#include "gpio_list.h"
+static void send_hid_event(void)
+{
+ uint64_t rpt = 0;
+ uint8_t *key_ptr = (void *)&rpt + 2;
+ /* Convert SW_N/SW_S/SW_W/SW_E to A,B,C,D keys */
+ if (gpio_get_level(GPIO_SW_N))
+ *key_ptr++ = 0x04; /* A keycode */
+ if (gpio_get_level(GPIO_SW_S))
+ *key_ptr++ = 0x05; /* B keycode */
+ if (gpio_get_level(GPIO_SW_W))
+ *key_ptr++ = 0x06; /* C keycode */
+ if (gpio_get_level(GPIO_SW_E))
+ *key_ptr++ = 0x07; /* D keycode */
+ /* send the keyboard state over USB HID */
+ set_keyboard_report(rpt);
+ /* check release in the future */
+ hook_call_deferred(send_hid_event, 40);
+}
+DECLARE_DEFERRED(send_hid_event);
+
/* Interrupt handler for button pushes */
void button_event(enum gpio_signal signal)
{
@@ -32,6 +55,7 @@ void button_event(enum gpio_signal signal)
signal -= (GPIO_SW_N_ - GPIO_SW_N);
v = gpio_get_level(signal);
+ send_hid_event();
ccprintf("Button %d = %d\n", signal, v);
gpio_set_level(signal - GPIO_SW_N + GPIO_LED_4, v);
}
@@ -49,3 +73,12 @@ static void board_init(void)
gpio_enable_interrupt(GPIO_SW_E_);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
+const void * const usb_strings[] = {
+ [USB_STR_DESC] = usb_string_desc,
+ [USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."),
+ [USB_STR_PRODUCT] = USB_STRING_DESC("Cr50"),
+ [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32),
+ [USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Shell"),
+};
+BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
diff --git a/board/cr50/board.h b/board/cr50/board.h
index e03544c8d5..2682ba83ab 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -15,6 +15,13 @@
#undef CONFIG_HIBERNATE
#undef CONFIG_LID_SWITCH
+/* USB configuration */
+#define CONFIG_USB
+#define CONFIG_USB_CONSOLE
+#define CONFIG_USB_HID
+
+#define CONFIG_USB_PID 0x5014
+
/*
* Allow dangerous commands all the time, since we don't have a write protect
* switch.
@@ -33,6 +40,28 @@
/* user button interrupt handler */
void button_event(enum gpio_signal signal);
+/* USB string indexes */
+enum usb_strings {
+ USB_STR_DESC = 0,
+ USB_STR_VENDOR,
+ USB_STR_PRODUCT,
+ USB_STR_VERSION,
+ USB_STR_CONSOLE_NAME,
+
+ USB_STR_COUNT
+};
+
#endif /* !__ASSEMBLER__ */
+/* USB interface indexes (use define rather than enum to expand them) */
+#define USB_IFACE_CONSOLE 0
+#define USB_IFACE_HID 1
+#define USB_IFACE_COUNT 2
+
+/* USB endpoint indexes (use define rather than enum to expand them) */
+#define USB_EP_CONTROL 0
+#define USB_EP_CONSOLE 1
+#define USB_EP_HID 2
+#define USB_EP_COUNT 3
+
#endif /* __BOARD_H */
diff --git a/chip/g/build.mk b/chip/g/build.mk
index ac626e0381..97dc18a6f1 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -19,3 +19,7 @@ CPPFLAGS+= -DGC_REVISION="$(ver_str)"
chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o
chip-y+= pmu.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
+
+chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
+chip-$(CONFIG_USB_CONSOLE)+=usb_console.o
+chip-$(CONFIG_USB_HID)+=usb_hid.o
diff --git a/chip/g/config_chip.h b/chip/g/config_chip.h
index 7a3fcb65df..d6dfe8fa13 100644
--- a/chip/g/config_chip.h
+++ b/chip/g/config_chip.h
@@ -52,4 +52,11 @@
/* Maximum number of deferrable functions */
#define DEFERRABLE_MAX_COUNT 8
+/* USB : TODO FIXME */
+#define CONFIG_USB_RAM_ACCESS_TYPE uint16_t
+/* No dedicated USB RAM */
+#define CONFIG_USB_RAM_BASE 0xdead0000
+#define CONFIG_USB_RAM_ACCESS_SIZE 0
+#define CONFIG_USB_RAM_SIZE 0
+
#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/g/registers.h b/chip/g/registers.h
index f5036efde0..c68be6aad6 100644
--- a/chip/g/registers.h
+++ b/chip/g/registers.h
@@ -359,4 +359,150 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer,
#define GR_XO_OSC_SETHOLD REG32(GC_XO0_BASE_ADDR + GC_XO_OSC_SETHOLD_OFFSET)
#define GR_XO_OSC_CLRHOLD REG32(GC_XO0_BASE_ADDR + GC_XO_OSC_CLRHOLD_OFFSET)
+/* USB device controller */
+#define GR_USB_REG(off) REG32(GC_USB0_BASE_ADDR + (off))
+#define GR_USB_GAHBCFG GR_USB_REG(GC_USB_GAHBCFG_OFFSET)
+#define GR_USB_GUSBCFG GR_USB_REG(GC_USB_GUSBCFG_OFFSET)
+#define GR_USB_GRSTCTL GR_USB_REG(GC_USB_GRSTCTL_OFFSET)
+#define GR_USB_GINTSTS GR_USB_REG(GC_USB_GINTSTS_OFFSET)
+#define GR_USB_GINTMSK GR_USB_REG(GC_USB_GINTMSK_OFFSET)
+#define GR_USB_GRXSTSR GR_USB_REG(GC_USB_GRXSTSR_OFFSET)
+#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_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)
+#define GR_USB_GHWCFG3 GR_USB_REG(GC_USB_GHWCFG3_OFFSET)
+#define GR_USB_GHWCFG4 GR_USB_REG(GC_USB_GHWCFG4_OFFSET)
+#define GR_USB_GDFIFOCFG GR_USB_REG(GC_USB_GDFIFOCFG_OFFSET)
+#define GR_USB_DIEPTXF(n) GR_USB_REG(GC_USB_DIEPTXF1_OFFSET - 4 + (n)*4)
+#define GR_USB_DCFG GR_USB_REG(GC_USB_DCFG_OFFSET)
+#define GR_USB_DCTL GR_USB_REG(GC_USB_DCTL_OFFSET)
+#define GR_USB_DSTS GR_USB_REG(GC_USB_DSTS_OFFSET)
+#define GR_USB_DIEPMSK GR_USB_REG(GC_USB_DIEPMSK_OFFSET)
+#define GR_USB_DOEPMSK GR_USB_REG(GC_USB_DOEPMSK_OFFSET)
+#define GR_USB_DAINT GR_USB_REG(GC_USB_DAINT_OFFSET)
+#define GR_USB_DAINTMSK GR_USB_REG(GC_USB_DAINTMSK_OFFSET)
+#define GR_USB_DTHRCTL GR_USB_REG(GC_USB_DTHRCTL_OFFSET)
+#define GR_USB_DIEPEMPMSK GR_USB_REG(GC_USB_DIEPEMPMSK_OFFSET)
+
+#define GR_USB_EPIREG(off, n) GR_USB_REG(0x900 + (n) * 0x20 + (off))
+#define GR_USB_EPOREG(off, n) GR_USB_REG(0xb00 + (n) * 0x20 + (off))
+#define GR_USB_DIEPCTL(n) GR_USB_EPIREG(0x00, n)
+#define GR_USB_DIEPINT(n) GR_USB_EPIREG(0x08, n)
+#define GR_USB_DIEPTSIZ(n) GR_USB_EPIREG(0x10, n)
+#define GR_USB_DIEPDMA(n) GR_USB_EPIREG(0x14, n)
+#define GR_USB_DTXFSTS(n) GR_USB_EPIREG(0x18, n)
+#define GR_USB_DIEPDMAB(n) GR_USB_EPIREG(0x1c, n)
+#define GR_USB_DOEPCTL(n) GR_USB_EPOREG(0x00, n)
+#define GR_USB_DOEPINT(n) GR_USB_EPOREG(0x08, n)
+#define GR_USB_DOEPTSIZ(n) GR_USB_EPOREG(0x10, n)
+#define GR_USB_DOEPDMA(n) GR_USB_EPOREG(0x14, n)
+#define GR_USB_DOEPDMAB(n) GR_USB_EPOREG(0x1c, n)
+
+#define GAHBCFG_DMA_EN (1 << GC_USB_GAHBCFG_DMAEN_LSB)
+#define GAHBCFG_GLB_INTR_EN (1 << GC_USB_GAHBCFG_GLBLINTRMSK_LSB)
+#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_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)
+#define GUSBCFG_FSINTF_3PIN (1 << GC_USB_GUSBCFG_FSINTF_LSB)
+#define GUSBCFG_PHYIF16 (1 << GC_USB_GUSBCFG_PHYIF_LSB)
+#define GUSBCFG_PHYIF8 (0 << GC_USB_GUSBCFG_PHYIF_LSB)
+#define GUSBCFG_ULPI (1 << GC_USB_GUSBCFG_ULPI_UTMI_SEL_LSB)
+#define GUSBCFG_UTMI (0 << GC_USB_GUSBCFG_ULPI_UTMI_SEL_LSB)
+
+#define GRSTCTL_CSFTRST (1 << GC_USB_GRSTCTL_CSFTRST_LSB)
+#define GRSTCTL_AHBIDLE (1 << GC_USB_GRSTCTL_AHBIDLE_LSB)
+#define GRSTCTL_TXFFLSH (1 << GC_USB_GRSTCTL_TXFFLSH_LSB)
+#define GRSTCTL_RXFFLSH (1 << GC_USB_GRSTCTL_RXFFLSH_LSB)
+#define GRSTCTL_TXFNUM(n) (((n) << GC_USB_GRSTCTL_TXFNUM_LSB) & GC_USB_GRSTCTL_TXFNUM_MASK)
+
+#define GINTSTS_RXFLVL (1 << GC_USB_GINTSTS_RXFLVL_LSB)
+#define GINTSTS_SOF (1 << GC_USB_GINTSTS_SOF_LSB)
+#define GINTSTS_GOUTNAKEFF (1 << GC_USB_GINTMSK_GOUTNAKEFFMSK_LSB)
+#define GINTSTS_GINNAKEFF (1 << GC_USB_GINTMSK_GINNAKEFFMSK_LSB)
+#define GINTSTS_USBRST (1 << GC_USB_GINTMSK_USBRSTMSK_LSB)
+#define GINTSTS_ENUMDONE (1 << GC_USB_GINTMSK_ENUMDONEMSK_LSB)
+#define GINTSTS_IEPINT (1 << GC_USB_GINTSTS_IEPINT_LSB)
+#define GINTSTS_OEPINT (1 << GC_USB_GINTSTS_OEPINT_LSB)
+
+#define DCFG_DEVSPD_FS (1 << GC_USB_DCFG_DEVSPD_LSB)
+#define DCFG_DEVSPD_FS48 (3 << GC_USB_DCFG_DEVSPD_LSB)
+#define DCFG_DEVADDR(a) (((a) << GC_USB_DCFG_DEVADDR_LSB) & GC_USB_DCFG_DEVADDR_MASK)
+#define DCFG_DESCDMA (1 << GC_USB_DCFG_DESCDMA_LSB)
+
+#define DCTL_SFTDISCON (1 << GC_USB_DCTL_SFTDISCON_LSB)
+#define DCTL_CGOUTNAK (1 << GC_USB_DCTL_CGOUTNAK_LSB)
+#define DCTL_CGNPINNAK (1 << GC_USB_DCTL_CGNPINNAK_LSB)
+#define DCTL_PWRONPRGDONE (1 << GC_USB_DCTL_PWRONPRGDONE_LSB)
+
+#define DIEPMSK_TIMEOUTMSK (1 << GC_USB_DIEPMSK_TIMEOUTMSK_LSB)
+#define DIEPMSK_AHBERRMSK (1 << GC_USB_DIEPMSK_AHBERRMSK_LSB)
+#define DIEPMSK_EPDISBLDMSK (1 << GC_USB_DIEPMSK_EPDISBLDMSK_LSB)
+#define DIEPMSK_XFERCOMPLMSK (1 << GC_USB_DIEPMSK_XFERCOMPLMSK_LSB)
+#define DIEPMSK_INTKNTXFEMPMSK (1 << GC_USB_DIEPMSK_INTKNTXFEMPMSK_LSB)
+#define DIEPMSK_INTKNEPMISMSK (1 << GC_USB_DIEPMSK_INTKNEPMISMSK_LSB)
+#define DOEPMSK_SETUPMSK (1 << GC_USB_DOEPMSK_SETUPMSK_LSB)
+#define DOEPMSK_AHBERRMSK (1 << GC_USB_DOEPMSK_AHBERRMSK_LSB)
+#define DOEPMSK_EPDISBLDMSK (1 << GC_USB_DOEPMSK_EPDISBLDMSK_LSB)
+#define DOEPMSK_XFERCOMPLMSK (1 << GC_USB_DOEPMSK_XFERCOMPLMSK_LSB)
+
+#define DXEPCTL_EPTYPE_CTRL (0 << GC_USB_DIEPCTL0_EPTYPE_LSB)
+#define DXEPCTL_EPTYPE_ISO (1 << GC_USB_DIEPCTL0_EPTYPE_LSB)
+#define DXEPCTL_EPTYPE_BULK (2 << GC_USB_DIEPCTL0_EPTYPE_LSB)
+#define DXEPCTL_EPTYPE_INT (3 << GC_USB_DIEPCTL0_EPTYPE_LSB)
+#define DXEPCTL_EPTYPE_MASK GC_USB_DIEPCTL0_EPTYPE_MASK
+#define DXEPCTL_TXFNUM(n) ((n) << GC_USB_DIEPCTL1_TXFNUM_LSB)
+#define DXEPCTL_STALL (1 << GC_USB_DIEPCTL0_STALL_LSB)
+#define DXEPCTL_CNAK (1 << GC_USB_DIEPCTL0_CNAK_LSB)
+#define DXEPCTL_DPID (1 << GC_USB_DIEPCTL0_DPID_LSB)
+#define DXEPCTL_SNAK (1 << GC_USB_DIEPCTL0_SNAK_LSB)
+#define DXEPCTL_NAKSTS (1 << GC_USB_DIEPCTL0_NAKSTS_LSB)
+#define DXEPCTL_EPENA (1 << GC_USB_DIEPCTL0_EPENA_LSB)
+#define DXEPCTL_EPDIS (1 << GC_USB_DIEPCTL0_EPDIS_LSB)
+#define DXEPCTL_USBACTEP (1 << GC_USB_DIEPCTL0_USBACTEP_LSB)
+#define DXEPCTL_MPS64 (0 << GC_USB_DIEPCTL0_MPS_LSB)
+#define DXEPCTL_MPS(cnt) ((cnt) << GC_USB_DIEPCTL1_MPS_LSB)
+
+#define DXEPTSIZ_SUPCNT(n) ((n) << GC_USB_DOEPTSIZ0_SUPCNT_LSB)
+#define DXEPTSIZ_PKTCNT(n) ((n) << GC_USB_DIEPTSIZ0_PKTCNT_LSB)
+#define DXEPTSIZ_XFERSIZE(n) ((n) << GC_USB_DIEPTSIZ0_XFERSIZE_LSB)
+
+#define DOEPDMA_BS_HOST_RDY (0 << 30)
+#define DOEPDMA_BS_DMA_BSY (1 << 30)
+#define DOEPDMA_BS_DMA_DONE (2 << 30)
+#define DOEPDMA_BS_HOST_BSY (3 << 30)
+#define DOEPDMA_BS_MASK (3 << 30)
+#define DOEPDMA_RXSTS_MASK (3 << 28)
+#define DOEPDMA_LAST (1 << 27)
+#define DOEPDMA_SP (1 << 26)
+#define DOEPDMA_IOC (1 << 25)
+#define DOEPDMA_SR (1 << 24)
+#define DOEPDMA_MTRF (1 << 23)
+#define DOEPDMA_NAK (1 << 16)
+#define DOEPDMA_RXBYTES(n) (((n) & 0xFFFF) << 0)
+#define DOEPDMA_RXBYTES_MASK (0xFFFF << 0)
+
+#define DIEPDMA_BS_HOST_RDY (0 << 30)
+#define DIEPDMA_BS_DMA_BSY (1 << 30)
+#define DIEPDMA_BS_DMA_DONE (2 << 30)
+#define DIEPDMA_BS_HOST_BSY (3 << 30)
+#define DIEPDMA_BS_MASK (3 << 30)
+#define DIEPDMA_TXSTS_MASK (3 << 28)
+#define DIEPDMA_LAST (1 << 27)
+#define DIEPDMA_SP (1 << 26)
+#define DIEPDMA_IOC (1 << 25)
+#define DIEPDMA_TXBYTES(n) (((n) & 0xFFFF) << 0)
+#define DIEPDMA_TXBYTES_MASK (0xFFFF << 0)
+
+struct g_usb_desc {
+ uint32_t flags;
+ void *addr;
+};
+
#endif /* __CROS_EC_REGISTERS_H */
diff --git a/chip/g/usb.c b/chip/g/usb.c
new file mode 100644
index 0000000000..1241089d61
--- /dev/null
+++ b/chip/g/usb.c
@@ -0,0 +1,461 @@
+/* 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 "clock.h"
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb.h"
+
+/* Rev A1 has a RTL bug in the FIFO */
+#if CONCAT2(GC_, GC___MAJOR_REV__) == GC___REVA__
+/*
+ * WORKAROUND: only the first 256 entries are usable as TX FIFO
+ *
+ * Use the last 128 entries for EP_INFO (not affected by the bug)
+ * and 256 entries for RX/TX FIFOs : total 384 entries.
+ *
+ * RX FIFO needs more than 64 entries (for reserved space)
+ * set RX FIFO to 80 entries
+ * set TX0-TX10 FIFO to 64 bytes = 16 (x 32-bit) entries
+ * let TX11-TX15 uninitialized for now (WORKAROUND).
+ */
+#define FIFO_SIZE 0x180
+#define TX_FIFO_CNT 11
+#else
+#define FIFO_SIZE 0x400
+#define TX_FIFO_CNT 16
+#endif
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+#ifdef CONFIG_USB_BOS
+/* v2.01 (vs 2.00) BOS Descriptor provided */
+#define USB_DEV_BCDUSB 0x0201
+#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 = 0,
+ .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, /* no of returned bytes, set at runtime */
+ .bNumInterfaces = USB_IFACE_COUNT,
+ .bConfigurationValue = 1,
+ .iConfiguration = USB_STR_VERSION,
+ .bmAttributes = 0x80, /* bus powered */
+ .bMaxPower = 250, /* MaxPower 500 mA */
+};
+
+const uint8_t usb_string_desc[] = {
+ 4, /* Descriptor size */
+ USB_DT_STRING,
+ 0x09, 0x04 /* LangID = 0x0409: U.S. English */
+};
+
+/* Descriptors for USB controller S/G DMA */
+struct g_usb_desc ep0_out_desc;
+struct g_usb_desc ep0_in_desc;
+
+/* Control endpoint (EP0) buffers */
+static usb_uint ep0_buf_tx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+static usb_uint ep0_buf_rx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+
+
+static int set_addr;
+/* remaining size of descriptor data to transfer */
+static int desc_left;
+/* pointer to descriptor data if any */
+static const uint8_t *desc_ptr;
+
+/* Requests on the control endpoint (aka EP0) */
+static void ep0_rx(void)
+{
+ uint32_t epint = GR_USB_DOEPINT(0);
+ uint16_t req = ep0_buf_rx[0]; /* bRequestType | bRequest */
+
+ GR_USB_DOEPINT(0) = epint; /* clear IT */
+
+ /* reset any incomplete descriptor transfer */
+ desc_ptr = NULL;
+
+ /* interface specific requests */
+ if ((req & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ uint8_t iface = ep0_buf_rx[2] & 0xff;
+ if (iface < USB_IFACE_COUNT &&
+ usb_iface_request[iface](ep0_buf_rx, ep0_buf_tx))
+ goto unknown_req;
+ return;
+ }
+
+ if (req == (USB_DIR_IN | (USB_REQ_GET_DESCRIPTOR << 8))) {
+ uint8_t type = ep0_buf_rx[1] >> 8;
+ uint8_t idx = ep0_buf_rx[1] & 0xff;
+ const uint8_t *desc;
+ int len;
+
+ switch (type) {
+ case USB_DT_DEVICE: /* Setup : Get device descriptor */
+ desc = (void *)&dev_desc;
+ len = sizeof(dev_desc);
+ break;
+ case USB_DT_CONFIGURATION: /* Setup : Get configuration desc */
+ desc = __usb_desc;
+ len = USB_DESC_SIZE;
+ break;
+#ifdef CONFIG_USB_BOS
+ case USB_DT_BOS: /* Setup : Get BOS descriptor */
+ desc = bos_ctx.descp;
+ len = bos_ctx.size;
+ break;
+#endif
+ case USB_DT_STRING: /* Setup : Get string descriptor */
+ if (idx >= USB_STR_COUNT)
+ /* The string does not exist : STALL */
+ goto unknown_req;
+ desc = usb_strings[idx];
+ len = desc[0];
+ break;
+ case USB_DT_DEVICE_QUALIFIER: /* Get device qualifier desc */
+ /* Not high speed : STALL next IN used as handshake */
+ goto unknown_req;
+ default: /* unhandled descriptor */
+ goto unknown_req;
+ }
+ /* do not send more than what the host asked for */
+ len = MIN(ep0_buf_rx[3], len);
+ /*
+ * if we cannot transmit everything at once,
+ * keep the remainder for the next IN packet
+ */
+ if (len >= USB_MAX_PACKET_SIZE) {
+ desc_left = len - USB_MAX_PACKET_SIZE;
+ desc_ptr = desc + USB_MAX_PACKET_SIZE;
+ len = USB_MAX_PACKET_SIZE;
+ }
+ memcpy_to_usbram(ep0_buf_tx, desc, len);
+ if (type == USB_DT_CONFIGURATION)
+ /* set the real descriptor size */
+ ep0_buf_tx[1] = USB_DESC_SIZE;
+ 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;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ /* send the null OUT transaction if the transfer is complete */
+ } else if (req == (USB_DIR_IN | (USB_REQ_GET_STATUS << 8))) {
+ uint16_t zero = 0;
+ /* Get status */
+ memcpy_to_usbram(ep0_buf_tx, (void *)&zero, 2);
+ ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(2);
+ GR_USB_DIEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST
+ | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ } else if ((req & 0xff) == USB_DIR_OUT) {
+ switch (req >> 8) {
+ case USB_REQ_SET_ADDRESS:
+ /* set the address after we got IN packet handshake */
+ set_addr = ep0_buf_rx[1] & 0xff;
+ /* need 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;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ /* uint8_t cfg = ep0_buf_rx[1] & 0xff; */
+ /* null IN for handshake */
+ 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;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ break;
+ default: /* unhandled request */
+ goto unknown_req;
+ }
+
+ } else {
+ goto unknown_req;
+ }
+
+ return;
+unknown_req:
+ ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST |
+ DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ GR_USB_DIEPCTL(0) |= DXEPCTL_STALL | DXEPCTL_EPENA;
+ return;
+}
+
+static void ep0_tx(void)
+{
+ uint32_t epint = GR_USB_DIEPINT(0);
+
+ GR_USB_DIEPINT(0) = epint; /* clear IT */
+
+ if (set_addr) {
+ GR_USB_DCFG = (GR_USB_DCFG & ~DCFG_DEVADDR(0x7f))
+ | DCFG_DEVADDR(set_addr);
+ CPRINTF("SETAD %02x\n", set_addr);
+ set_addr = 0;
+ }
+ if (desc_ptr) {
+ /* we have an on-going descriptor transfer */
+ int len = MIN(desc_left, USB_MAX_PACKET_SIZE);
+ memcpy_to_usbram(ep0_buf_tx, desc_ptr, len);
+ ep0_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY |
+ DIEPDMA_IOC | DIEPDMA_TXBYTES(len);
+ desc_left -= len;
+ desc_ptr += len;
+ /* send the null OUT transaction if the transfer is complete */
+ GR_USB_DIEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ /* TODO set Data PID in DIEPCTL */
+ return;
+ }
+}
+
+static void ep0_reset(void)
+{
+ ep0_out_desc.flags = DOEPDMA_RXBYTES(64) | DOEPDMA_LAST |
+ DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ ep0_out_desc.addr = ep0_buf_rx;
+ ep0_in_desc.flags = DIEPDMA_TXBYTES(0) | DIEPDMA_LAST |
+ DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC;
+ ep0_in_desc.addr = ep0_buf_tx;
+ GR_USB_DIEPDMA(0) = (uint32_t)&ep0_in_desc;
+ GR_USB_DOEPDMA(0) = (uint32_t)&ep0_out_desc;
+ GR_USB_DOEPCTL(0) = DXEPCTL_MPS64 | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_CTRL |
+ DXEPCTL_CNAK | DXEPCTL_EPENA;
+ GR_USB_DIEPCTL(0) = DXEPCTL_MPS64 | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_CTRL;
+ GR_USB_DAINTMSK = (1<<0) | (1 << (0+16)); /* EPO interrupts */
+}
+USB_DECLARE_EP(0, ep0_tx, ep0_rx, ep0_reset);
+
+static void usb_reset(void)
+{
+ int ep;
+
+ for (ep = 0; ep < USB_EP_COUNT; ep++)
+ usb_ep_reset[ep]();
+
+ /*
+ * set the default address : 0
+ * as we are not configured yet
+ */
+ GR_USB_DCFG &= ~DCFG_DEVADDR(0x7f);
+}
+
+void usb_interrupt(void)
+{
+ uint32_t status = GR_USB_GINTSTS;
+
+ if (status & GINTSTS_USBRST)
+ usb_reset();
+
+ if (status & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
+ uint32_t daint = GR_USB_DAINT;
+ int ep;
+ for (ep = 0; ep < USB_EP_COUNT && daint; ep++, daint >>= 1) {
+ if (daint & (1 << 16)) /* OUT packet */
+ usb_ep_rx[ep]();
+ if (daint & 1) /* IN packet */
+ usb_ep_tx[ep]();
+ }
+ }
+
+ if (status & GINTSTS_GOUTNAKEFF)
+ GR_USB_DCTL = DCTL_CGOUTNAK;
+
+ if (status & GINTSTS_GINNAKEFF)
+ GR_USB_DCTL = DCTL_CGNPINNAK;
+
+ /* ack interrupts */
+ GR_USB_GINTSTS = status;
+}
+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;
+ }
+}
+
+void usb_connect(void)
+{
+ GR_USB_DCTL &= ~DCTL_SFTDISCON;
+}
+
+void usb_disconnect(void)
+{
+ GR_USB_DCTL |= DCTL_SFTDISCON;
+}
+
+void usb_init(void)
+{
+ int i;
+ /* Enable clocks */
+ clock_enable_module(MODULE_USB, 1);
+
+ /* set up pinmux */
+ gpio_config_module(MODULE_USB, 1);
+
+ /* Use the last 128 entries of the FIFO for EP INFO */
+ GR_USB_GDFIFOCFG = ((FIFO_SIZE - 0x80) << 16) | FIFO_SIZE;
+
+ /* PHY configuration */
+ /* Full-Speed Serial PHY */
+ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN
+ | GUSBCFG_TOUTCAL(7) | (9 << 10);
+ usb_softreset();
+
+ /* PHY configuration */
+ /* Full-Speed Serial PHY */
+ GR_USB_GUSBCFG = GUSBCFG_PHYSEL_FS | GUSBCFG_FSINTF_6PIN
+ | GUSBCFG_TOUTCAL(7) | (9 << 10);
+ /* Global + DMA configuration */
+ GR_USB_GAHBCFG = GAHBCFG_DMA_EN | GAHBCFG_GLB_INTR_EN |
+ GAHBCFG_NP_TXF_EMP_LVL;
+
+ /* unmask subset of endpoint interrupts */
+ GR_USB_DIEPMSK = DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
+ DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
+ DIEPMSK_INTKNEPMISMSK /*| (1<<9)*//*BNA*/;
+ GR_USB_DOEPMSK = DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK |
+ DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK;
+ GR_USB_DAINTMSK = 0;
+
+ /* Be in disconnected state we are ready */
+ GR_USB_DCTL |= DCTL_SFTDISCON;
+
+ /* Max speed: USB2 FS */
+ GR_USB_DCFG = DCFG_DEVSPD_FS48 | DCFG_DESCDMA;
+
+ /* clear pending interrupts */
+ GR_USB_GINTSTS = 0xFFFFFFFF;
+
+ /*
+ * Setup FIFOs configuration
+ * RX FIFO needs more than 64 entries (for reserved space)
+ * set RX FIFO to 80 entries
+ * set TX FIFO to 64 bytes = 16 (x 32-bit) entries
+ */
+ GR_USB_GRXFSIZ = 80;
+ GR_USB_GNPTXFSIZ = 80 | (16 << 16);
+ for (i = 1; i < TX_FIFO_CNT; i++)
+ GR_USB_DIEPTXF(i) = (80 + i*16) | (16 << 16);
+ /* Flush all FIFOs */
+ GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH
+ | GRSTCTL_RXFFLSH;
+ while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH))
+ ; /* timeout 100ms */
+
+ /* Initialize endpoints */
+ for (i = 0; i < 16; i++) {
+ GR_USB_DIEPCTL(i) = 0x00/* TODO */;
+ GR_USB_DOEPCTL(i) = 0x00/* TODO */;
+ }
+
+ /* 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;
+
+ /* Enable interrupt handlers */
+ task_enable_irq(GC_IRQNUM_USB0_USBINTR);
+ /* set interrupts mask : reset/correct tranfer/errors */
+ GR_USB_GINTMSK = GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
+ GINTSTS_USBRST | GINTSTS_ENUMDONE |
+ GINTSTS_OEPINT | GINTSTS_IEPINT;
+
+#ifndef CONFIG_USB_INHIBIT_CONNECT
+ usb_connect();
+#endif
+
+ CPRINTF("USB init done\n");
+}
+#ifndef CONFIG_USB_INHIBIT_INIT
+DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT);
+#endif
+
+void usb_release(void)
+{
+ /* signal disconnect to host */
+ usb_disconnect();
+
+ /* disable interrupt handlers */
+ task_disable_irq(GC_IRQNUM_USB0_USBINTR);
+
+ /* disable clocks */
+ clock_enable_module(MODULE_USB, 0);
+ /* TODO: pin-mux */
+}
+
+void *memcpy_to_usbram(void *dest, const void *src, size_t n)
+{
+ return memcpy(dest, src, n);
+}
diff --git a/chip/g/usb_console.c b/chip/g/usb_console.c
new file mode 100644
index 0000000000..6f84001007
--- /dev/null
+++ b/chip/g/usb_console.c
@@ -0,0 +1,266 @@
+/* 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 "common.h"
+#include "config.h"
+#include "console.h"
+#include "link_defs.h"
+#include "printf.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb.h"
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+#define USB_CONSOLE_TIMEOUT_US (30 * MSEC)
+#define USB_CONSOLE_RX_BUF_SIZE 16
+#define RX_BUF_NEXT(i) (((i) + 1) & (USB_CONSOLE_RX_BUF_SIZE - 1))
+
+static volatile char rx_buf[USB_CONSOLE_RX_BUF_SIZE];
+static volatile int rx_buf_head;
+static volatile int rx_buf_tail;
+
+static int last_tx_ok = 1;
+
+static int is_reset;
+static int is_enabled = 1;
+
+/* USB-Serial descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) =
+{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_CONSOLE,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = USB_SUBCLASS_GOOGLE_SERIAL,
+ .bInterfaceProtocol = USB_PROTOCOL_GOOGLE_SERIAL,
+ .iInterface = USB_STR_CONSOLE_NAME,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 0) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x80 | USB_EP_CONSOLE,
+ .bmAttributes = 0x02 /* Bulk IN */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 10
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 1) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_EP_CONSOLE,
+ .bmAttributes = 0x02 /* Bulk OUT */,
+ .wMaxPacketSize = USB_MAX_PACKET_SIZE,
+ .bInterval = 0
+};
+
+static usb_uint ep_buf_tx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+static usb_uint ep_buf_rx[USB_MAX_PACKET_SIZE / 2] /*__usb_ram*/;
+static struct g_usb_desc ep_out_desc;
+static struct g_usb_desc ep_in_desc;
+
+static void con_ep_tx(void)
+{
+ /* clear IT */
+ GR_USB_DIEPINT(USB_EP_CONSOLE) = 0xffffffff;
+}
+
+static void con_ep_rx(void)
+{
+ int i;
+ int rx_size = USB_MAX_PACKET_SIZE
+ - (ep_out_desc.flags & DOEPDMA_RXBYTES_MASK);
+
+ for (i = 0; i < rx_size; i++) {
+ int rx_buf_next = RX_BUF_NEXT(rx_buf_head);
+ if (rx_buf_next != rx_buf_tail) {
+ rx_buf[rx_buf_head] = ((i & 1) ?
+ (ep_buf_rx[i >> 1] >> 8) :
+ (ep_buf_rx[i >> 1] & 0xff));
+ rx_buf_head = rx_buf_next;
+ }
+ }
+
+ ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ GR_USB_DOEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ /* clear IT */
+ GR_USB_DOEPINT(USB_EP_CONSOLE) = 0xffffffff;
+
+ /* wake-up the console task */
+ console_has_input();
+}
+
+static void ep_reset(void)
+{
+ ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) |
+ DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC;
+ ep_out_desc.addr = ep_buf_rx;
+ GR_USB_DOEPDMA(USB_EP_CONSOLE) = (uint32_t)&ep_out_desc;
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
+ ep_in_desc.addr = ep_buf_tx;
+ GR_USB_DIEPDMA(USB_EP_CONSOLE) = (uint32_t)&ep_in_desc;
+ GR_USB_DOEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_CNAK | DXEPCTL_EPENA;
+ GR_USB_DIEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP |
+ DXEPCTL_EPTYPE_BULK |
+ DXEPCTL_TXFNUM(USB_EP_CONSOLE);
+ GR_USB_DAINTMSK |= (1<<USB_EP_CONSOLE) | (1 << (USB_EP_CONSOLE+16));
+
+ is_reset = 1;
+}
+
+USB_DECLARE_EP(USB_EP_CONSOLE, con_ep_tx, con_ep_rx, ep_reset);
+
+static int __tx_char(void *context, int c)
+{
+ usb_uint *buf = (usb_uint *)ep_buf_tx;
+ int *tx_idx = context;
+
+ /* Do newline to CRLF translation */
+ if (c == '\n' && __tx_char(context, '\r'))
+ return 1;
+
+ if (*tx_idx > 63)
+ return 1;
+ if (!(*tx_idx & 1))
+ buf[*tx_idx/2] = c;
+ else
+ buf[*tx_idx/2] |= c << 8;
+ (*tx_idx)++;
+
+ return 0;
+}
+
+static void usb_enable_tx(int len)
+{
+ if (!is_enabled)
+ return;
+
+ ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(len);
+ GR_USB_DIEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+static inline int usb_console_tx_valid(void)
+{
+ return (ep_in_desc.flags & DIEPDMA_BS_MASK) == DIEPDMA_BS_DMA_DONE;
+}
+
+static int usb_wait_console(void)
+{
+ timestamp_t deadline = get_time();
+ int wait_time_us = 1;
+
+ deadline.val += USB_CONSOLE_TIMEOUT_US;
+
+ /*
+ * If the USB console is not used, Tx buffer would never free up.
+ * In this case, let's drop characters immediately instead of sitting
+ * for some time just to time out. On the other hand, if the last
+ * Tx is good, it's likely the host is there to receive data, and
+ * we should wait so that we don't clobber the buffer.
+ */
+ if (last_tx_ok) {
+ while (usb_console_tx_valid() || !is_reset) {
+ if (timestamp_expired(deadline, NULL) ||
+ in_interrupt_context()) {
+ last_tx_ok = 0;
+ return EC_ERROR_TIMEOUT;
+ }
+ if (wait_time_us < MSEC)
+ udelay(wait_time_us);
+ else
+ usleep(wait_time_us);
+ wait_time_us *= 2;
+ }
+
+ return EC_SUCCESS;
+ } else {
+ last_tx_ok = !usb_console_tx_valid();
+ return EC_SUCCESS;
+ }
+}
+
+/*
+ * Public USB console implementation below.
+ */
+int usb_getc(void)
+{
+ int c;
+
+ if (rx_buf_tail == rx_buf_head)
+ return -1;
+
+ if (!is_enabled)
+ return -1;
+
+ c = rx_buf[rx_buf_tail];
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+ return c;
+}
+
+int usb_putc(int c)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ ret = __tx_char(&tx_idx, c);
+ usb_enable_tx(tx_idx);
+
+ return ret;
+}
+
+int usb_puts(const char *outstr)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ /* Put all characters in the output buffer */
+ while (*outstr) {
+ if (__tx_char(&tx_idx, *outstr++) != 0)
+ break;
+ }
+
+ usb_enable_tx(tx_idx);
+
+ /* Successful if we consumed all output */
+ return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
+}
+
+int usb_vprintf(const char *format, va_list args)
+{
+ int ret;
+ int tx_idx = 0;
+
+ ret = usb_wait_console();
+ if (ret)
+ return ret;
+
+ ret = vfnprintf(__tx_char, &tx_idx, format, args);
+
+ usb_enable_tx(tx_idx);
+ return ret;
+}
+
+void usb_console_enable(int enabled)
+{
+ is_enabled = enabled;
+}
diff --git a/chip/g/usb_endpoints.S b/chip/g/usb_endpoints.S
new file mode 100644
index 0000000000..cc13cadeb3
--- /dev/null
+++ b/chip/g/usb_endpoints.S
@@ -0,0 +1,107 @@
+/* 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.
+ *
+ * USB endpoints/interfaces callbacks declaration
+ */
+
+#include "config.h"
+
+.section .rodata.usb_ep
+
+.macro endpoint number suffix
+.if \number < USB_EP_COUNT
+.long ep_\number\()_\()\suffix
+.weak ep_\number\()_\()\suffix
+.set ep_\number\()_\()\suffix, ep_undefined
+.endif
+.endm
+
+.macro interface number
+.if \number < USB_IFACE_COUNT
+.long iface_\number\()_request
+.weak iface_\number\()_request
+.set iface_\number\()_request, iface_undefined
+.endif
+.endm
+
+/* align function pointers on a 32-bit boundary */
+.align 2
+/* Endpoint callbacks */
+.global usb_ep_tx
+usb_ep_tx:
+endpoint 0 tx
+endpoint 1 tx
+endpoint 2 tx
+endpoint 3 tx
+endpoint 4 tx
+endpoint 5 tx
+endpoint 6 tx
+endpoint 7 tx
+endpoint 8 tx
+endpoint 9 tx
+endpoint 10 tx
+endpoint 11 tx
+endpoint 12 tx
+endpoint 13 tx
+endpoint 14 tx
+endpoint 15 tx
+
+.global usb_ep_rx
+usb_ep_rx:
+endpoint 0 rx
+endpoint 1 rx
+endpoint 2 rx
+endpoint 3 rx
+endpoint 4 rx
+endpoint 5 rx
+endpoint 6 rx
+endpoint 7 rx
+endpoint 8 rx
+endpoint 9 rx
+endpoint 10 rx
+endpoint 11 rx
+endpoint 12 rx
+endpoint 13 rx
+endpoint 14 rx
+endpoint 15 rx
+
+.global usb_ep_reset
+usb_ep_reset:
+endpoint 0 rst
+endpoint 1 rst
+endpoint 2 rst
+endpoint 3 rst
+endpoint 4 rst
+endpoint 5 rst
+endpoint 6 rst
+endpoint 7 rst
+endpoint 8 rst
+endpoint 9 rst
+endpoint 10 rst
+endpoint 11 rst
+endpoint 12 rst
+endpoint 13 rst
+endpoint 14 rst
+endpoint 15 rst
+
+.global usb_iface_request
+usb_iface_request:
+interface 0
+interface 1
+interface 2
+interface 3
+interface 4
+interface 5
+interface 6
+interface 7
+
+.text
+.code 16
+
+.thumb_func
+/* Undefined interface callbacks fail by returning non-zero*/
+iface_undefined:
+ mov r0, #1
+ep_undefined:
+ bx lr
diff --git a/chip/g/usb_hid.c b/chip/g/usb_hid.c
new file mode 100644
index 0000000000..5a34f491a3
--- /dev/null
+++ b/chip/g/usb_hid.c
@@ -0,0 +1,170 @@
+/* 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 "clock.h"
+#include "common.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "link_defs.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb.h"
+#include "usb_hid.h"
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+#define HID_REPORT_SIZE 8
+
+/* HID descriptors */
+const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_HID) =
+{
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = USB_IFACE_HID,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = USB_HID_SUBCLASS_BOOT,
+ .bInterfaceProtocol = USB_HID_PROTOCOL_KEYBOARD,
+ .iInterface = 0,
+};
+const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_HID, 81) =
+{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x80 | USB_EP_HID,
+ .bmAttributes = 0x03 /* Interrupt endpoint */,
+ .wMaxPacketSize = HID_REPORT_SIZE,
+ .bInterval = 32 /* ms polling interval */
+};
+const struct usb_hid_descriptor USB_CUSTOM_DESC(USB_IFACE_HID, hid) =
+{
+ .bLength = 9,
+ .bDescriptorType = USB_HID_DT_HID,
+ .bcdHID = 0x0100,
+ .bCountryCode = 0x00, /* Hardware target country */
+ .bNumDescriptors = 1,
+ .desc = {
+ {.bDescriptorType = USB_HID_DT_REPORT,
+ .wDescriptorLength = 45}
+ }
+};
+
+/* HID : Report Descriptor */
+static const uint8_t report_desc[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0xE0, /* Usage Minimum (224) */
+ 0x29, 0xE7, /* Usage Maximum (231) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute), ;Modifier byte */
+
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x81, 0x01, /* Input (Constant), ;Reserved byte */
+
+ 0x95, 0x06, /* Report Count (6) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x65, /* Logical Maximum(101) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x29, 0x65, /* Usage Maximum (101) */
+ 0x81, 0x00, /* Input (Data, Array), ;Key arrays (6 bytes) */
+ 0xC0, /* End Collection */
+ 0x00 /* Padding */
+};
+
+static usb_uint hid_ep_buf[HID_REPORT_SIZE / 2] /*__usb_ram*/;
+static struct g_usb_desc hid_ep_desc;
+
+void set_keyboard_report(uint64_t rpt)
+{
+ memcpy_to_usbram(hid_ep_buf, (const uint8_t *)&rpt, sizeof(rpt));
+ hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
+ DIEPDMA_TXBYTES(HID_REPORT_SIZE);
+ /* enable TX */
+ GR_USB_DIEPCTL(USB_EP_HID) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+}
+
+static void hid_tx(void)
+{
+ /* clear IT */
+ GR_USB_DIEPINT(USB_EP_HID) = 0xffffffff;
+ return;
+}
+
+static void hid_reset(void)
+{
+ hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
+ hid_ep_desc.addr = hid_ep_buf;
+ GR_USB_DIEPDMA(USB_EP_HID) = (uint32_t)&hid_ep_desc;
+ GR_USB_DIEPCTL(USB_EP_HID) = DXEPCTL_MPS(HID_REPORT_SIZE) |
+ DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_INT |
+ DXEPCTL_TXFNUM(USB_EP_HID);
+ GR_USB_DAINTMSK |= (1<<USB_EP_CONSOLE);
+}
+
+USB_DECLARE_EP(USB_EP_HID, hid_tx, hid_tx, hid_reset);
+
+extern struct g_usb_desc ep0_in_desc;
+extern struct g_usb_desc ep0_out_desc;
+
+static int hid_iface_request(usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx)
+{
+ int len;
+
+ if ((ep0_buf_rx[0] == (USB_DIR_IN | USB_RECIP_INTERFACE |
+ (USB_REQ_GET_DESCRIPTOR << 8))) &&
+ (ep0_buf_rx[1] == (USB_HID_DT_REPORT << 8))) {
+ /* Setup : HID specific : Get Report descriptor */
+ memcpy_to_usbram(ep0_buf_tx, report_desc,
+ sizeof(report_desc));
+ len = MIN(ep0_buf_rx[3], sizeof(report_desc));
+ 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;
+ GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
+ return 0;
+ }
+
+ return 1;
+}
+USB_DECLARE_IFACE(USB_IFACE_HID, hid_iface_request)
+
+static int command_hid(int argc, char **argv)
+{
+ uint8_t keycode = 0x0a; /* 'G' key */
+
+ if (argc >= 2) {
+ char *e;
+ keycode = strtoi(argv[1], &e, 16);
+ if (*e)
+ return EC_ERROR_PARAM1;
+ }
+
+ /* press then release the key */
+ set_keyboard_report((uint32_t)keycode << 16);
+ udelay(50000);
+ set_keyboard_report(0x000000);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(hid, command_hid,
+ "[<HID keycode>]",
+ "test USB HID driver",
+ NULL);