diff options
-rw-r--r-- | board/cr50/board.c | 33 | ||||
-rw-r--r-- | board/cr50/board.h | 29 | ||||
-rw-r--r-- | chip/g/build.mk | 4 | ||||
-rw-r--r-- | chip/g/config_chip.h | 7 | ||||
-rw-r--r-- | chip/g/registers.h | 146 | ||||
-rw-r--r-- | chip/g/usb.c | 461 | ||||
-rw-r--r-- | chip/g/usb_console.c | 266 | ||||
-rw-r--r-- | chip/g/usb_endpoints.S | 107 | ||||
-rw-r--r-- | chip/g/usb_hid.c | 170 |
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); |