mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-03 13:39:53 +00:00
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>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
de24d51162
commit
4d0aad8894
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
461
chip/g/usb.c
Normal file
461
chip/g/usb.c
Normal file
@@ -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);
|
||||
}
|
||||
266
chip/g/usb_console.c
Normal file
266
chip/g/usb_console.c
Normal file
@@ -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;
|
||||
}
|
||||
107
chip/g/usb_endpoints.S
Normal file
107
chip/g/usb_endpoints.S
Normal file
@@ -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
|
||||
170
chip/g/usb_hid.c
Normal file
170
chip/g/usb_hid.c
Normal file
@@ -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);
|
||||
Reference in New Issue
Block a user