summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2013-06-05 17:15:05 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-06-18 06:08:45 +0000
commit5fa52a895b6acd5d976a24dd571a44eaa3cb1ad1 (patch)
tree667ccc50249a31eec51199811b9fd3780ba52729
parent4957dcbd87cd75d91bca772fef93ec2e0256ea4f (diff)
downloadchrome-ec-5fa52a895b6acd5d976a24dd571a44eaa3cb1ad1.tar.gz
stm32: add USB driver
Enough USB support to be able to enumerate the device and use bulk or interrupt endpoints. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:28295 TEST=with the following USB console CL, connect a Fruitpie through USB and use its console over USB. Change-Id: I37b7f3b6a754cb82ab5f940ea20122d2e16b3b5b Reviewed-on: https://chromium-review.googlesource.com/193983 Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Todd Broch <tbroch@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--Makefile.toolchain2
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/config_chip.h4
-rw-r--r--chip/stm32/registers.h40
-rw-r--r--chip/stm32/usb.c263
-rw-r--r--chip/stm32/usb_endpoints.S101
-rw-r--r--common/console_output.c1
-rw-r--r--core/cortex-m/ec.lds.S20
-rw-r--r--core/cortex-m0/ec.lds.S20
-rw-r--r--include/config.h3
-rw-r--r--include/console.h1
-rw-r--r--include/link_defs.h5
-rw-r--r--include/module_id.h1
-rw-r--r--include/usb.h245
14 files changed, 704 insertions, 3 deletions
diff --git a/Makefile.toolchain b/Makefile.toolchain
index dd0ebb2174..6bf27a3f78 100644
--- a/Makefile.toolchain
+++ b/Makefile.toolchain
@@ -40,7 +40,7 @@ CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DBOARD_TASKFILE=ec.tasklist \
CPPFLAGS=$(CFLAGS_DEFINE) $(CFLAGS_INCLUDE) $(CFLAGS_TEST) \
$(EXTRA_CFLAGS) $(CFLAGS_COVERAGE)
CFLAGS=$(CPPFLAGS) $(CFLAGS_CPU) $(CFLAGS_DEBUG) $(CFLAGS_WARN) $(CFLAGS_y)
-CFLAGS+= -ffunction-sections
+CFLAGS+= -ffunction-sections -fshort-wchar
FTDIVERSION=$(shell $(PKG_CONFIG) --modversion libftdi1 2>/dev/null)
ifneq ($(FTDIVERSION),)
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index aa8e08b32c..f420fed742 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -37,4 +37,5 @@ chip-$(HAS_TASK_POWERLED)+=power_led.o
chip-$(CONFIG_FLASH)+=flash-$(FLASH_FAMILY).o
chip-$(CONFIG_ADC)+=adc-$(CHIP_FAMILY).o
chip-$(CONFIG_PWM)+=pwm.o
+chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
chip-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_phy.o
diff --git a/chip/stm32/config_chip.h b/chip/stm32/config_chip.h
index b2479f21b9..366311d09b 100644
--- a/chip/stm32/config_chip.h
+++ b/chip/stm32/config_chip.h
@@ -85,4 +85,8 @@
/* Compile for running from RAM instead of flash */
/* #define COMPILE_FOR_RAM */
+/* Dedicated SRAM region for USB transfers */
+#define CONFIG_USB_RAM_BASE 0x40006000
+#define CONFIG_USB_RAM_SIZE 512
+
#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index 4e9dbd1419..c21dc50754 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -46,6 +46,7 @@
#define STM32_IRQ_USB 31
/* aliases for easier code sharing */
#define STM32_IRQ_COMP STM32_IRQ_ADC_COMP
+#define STM32_IRQ_USB_LP STM32_IRQ_USB
#else /* !CHIP_FAMILY_STM32F0 */
#define STM32_IRQ_WWDG 0
@@ -526,6 +527,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_RCC_PB2_TIM9 (1 << 2)
#define STM32_RCC_PB2_TIM10 (1 << 3)
#define STM32_RCC_PB2_TIM11 (1 << 4)
+#define STM32_RCC_PB1_USB (1 << 23)
#define STM32_SYSCFG_BASE 0x40010000
@@ -558,6 +560,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_RCC_PB2_PMAD (1 << 11) /* STM32TS */
#define STM32_RCC_PB2_PMSE (1 << 13) /* STM32TS */
#define STM32_RCC_PB1_TIM14 (1 << 8) /* STM32F0XX */
+#define STM32_RCC_PB1_USB (1 << 23)
#define STM32_SYSCFG_BASE 0x40010000
@@ -1212,6 +1215,41 @@ typedef volatile struct stm32_dma_regs stm32_dma_regs_t;
#define STM32_PMSE_MRCR REG32(STM32_PMSE_BASE + 0x100)
#define STM32_PMSE_MCCR REG32(STM32_PMSE_BASE + 0x104)
+/* --- USB --- */
+#define STM32_USB_CAN_SRAM_BASE 0x40006000
+#define STM32_USB_FS_BASE 0x40005C00
+
+#define STM32_USB_EP(n) REG16(STM32_USB_FS_BASE + (n) * 4)
+
+#define STM32_USB_CNTR REG16(STM32_USB_FS_BASE + 0x40)
+#define STM32_USB_ISTR REG16(STM32_USB_FS_BASE + 0x44)
+#define STM32_USB_FNR REG16(STM32_USB_FS_BASE + 0x48)
+#define STM32_USB_DADDR REG16(STM32_USB_FS_BASE + 0x4C)
+#define STM32_USB_BTABLE REG16(STM32_USB_FS_BASE + 0x50)
+#define STM32_USB_LPMCSR REG16(STM32_USB_FS_BASE + 0x54)
+#define STM32_USB_BCDR REG16(STM32_USB_FS_BASE + 0x58)
+
+#define EP_MASK 0x0F0F
+#define EP_TX_MASK 0x0030
+#define EP_TX_VALID 0x0030
+#define EP_TX_NAK 0x0020
+#define EP_TX_STALL 0x0010
+#define EP_TX_DISAB 0x0000
+#define EP_RX_MASK 0x3000
+#define EP_RX_VALID 0x3000
+#define EP_RX_NAK 0x2000
+#define EP_RX_STALL 0x1000
+#define EP_RX_DISAB 0x0000
+
+#define EP_STATUS_OUT 0x0100
+
+#define EP_TX_RX_MASK (EP_TX_MASK | EP_RX_MASK)
+#define EP_TX_RX_VALID (EP_TX_VALID | EP_RX_VALID)
+
+#define STM32_TOGGLE_EP(n, mask, val, flags) \
+ STM32_USB_EP(n) = (((STM32_USB_EP(n) & (EP_MASK | (mask))) \
+ ^ (val)) | (flags))
+
/* --- MISC --- */
#define STM32_CEC_BASE 0x40007800 /* STM32F100 only */
@@ -1222,7 +1260,5 @@ typedef volatile struct stm32_dma_regs stm32_dma_regs_t;
#define STM32_SDIO_BASE 0x40018000 /* STM32F10x only */
#define STM32_BXCAN1_BASE 0x40006400 /* STM32F10x only */
#define STM32_BXCAN2_BASE 0x40006800 /* STM32F10x only */
-#define STM32_USB_CAN_SRAM_BASE 0x40006000 /* STM32F10x only */
-#define STM32_USB_FS_BASE 0x40005C00 /* STM32F10x only */
#endif /* __CROS_EC_REGISTERS_H */
diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c
new file mode 100644
index 0000000000..9731fef9c1
--- /dev/null
+++ b/chip/stm32/usb.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2013 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"
+
+/* Console output macro */
+#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
+
+/* USB Standard Device Descriptor */
+static const struct usb_device_descriptor dev_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200, /* v2.00 */
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = USB_MAX_PACKET_SIZE,
+ .idVendor = USB_VID_GOOGLE,
+ .idProduct = CONFIG_USB_PID,
+ .bcdDevice = 0x0200, /* 2.00 */
+ .iManufacturer = USB_STR_VENDOR,
+ .iProduct = USB_STR_PRODUCT,
+ .iSerialNumber = USB_STR_VERSION,
+ .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 = 0,
+ .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 */
+};
+
+/* Endpoint table in USB controller RAM */
+struct stm32_endpoint btable_ep[USB_EP_COUNT]
+ __attribute__((section(".usb_ram.btable")));
+/* 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;
+
+/* Requests on the control endpoint (aka EP0) */
+static void ep0_rx(void)
+{
+ uint16_t req = ep0_buf_rx[0]; /* bRequestType | bRequest */
+
+ /* interface specific requests */
+ if ((req & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ uint8_t iface = ep0_buf_rx[1] & 0xff;
+ if (iface < USB_IFACE_COUNT)
+ usb_iface_request[iface](ep0_buf_rx, ep0_buf_tx);
+ return;
+ }
+
+ /* TODO check setup bit ? */
+ 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 *str_desc;
+
+ switch (type) {
+ case USB_DT_DEVICE: /* Setup : Get device descriptor */
+ memcpy_usbram(ep0_buf_tx, (void *)&dev_desc,
+ sizeof(dev_desc));
+ btable_ep[0].tx_count = sizeof(dev_desc);
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
+ EP_STATUS_OUT /*null OUT transaction */);
+ break;
+ case USB_DT_CONFIGURATION: /* Setup : Get configuration desc */
+ memcpy_usbram(ep0_buf_tx, __usb_desc,
+ USB_DESC_SIZE);
+ /* set the real descriptor size */
+ ep0_buf_tx[1] = USB_DESC_SIZE;
+ btable_ep[0].tx_count = MIN(ep0_buf_rx[3],
+ USB_DESC_SIZE);
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
+ EP_STATUS_OUT /*null OUT transaction */);
+ break;
+ case USB_DT_STRING: /* Setup : Get string descriptor */
+ if (idx >= USB_STR_COUNT) {
+ /* The string does not exist : STALL */
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK,
+ EP_RX_VALID | EP_TX_STALL, 0);
+ return; /* don't remove the STALL */
+ }
+ str_desc = usb_strings[idx];
+ memcpy_usbram(ep0_buf_tx, str_desc, str_desc[0]);
+ btable_ep[0].tx_count = MIN(ep0_buf_rx[3], str_desc[0]);
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
+ EP_STATUS_OUT /*null OUT transaction */);
+ break;
+ case USB_DT_DEVICE_QUALIFIER: /* Get device qualifier desc */
+ /* Not high speed : STALL next IN used as handshake */
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK,
+ EP_RX_VALID | EP_TX_STALL, 0);
+ break;
+ default: /* unhandled descriptor */
+ goto unknown_req;
+ }
+ } else if (req == (USB_DIR_IN | (USB_REQ_GET_STATUS << 8))) {
+ uint16_t zero = 0;
+ /* Get status */
+ memcpy_usbram(ep0_buf_tx, (void *)&zero, 2);
+ btable_ep[0].tx_count = 2;
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
+ EP_STATUS_OUT /*null OUT transaction */);
+ } 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 */
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, 0);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ /* uint8_t cfg = ep0_buf_rx[1] & 0xff; */
+ /* null IN for handshake */
+ btable_ep[0].tx_count = 0;
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, 0);
+ break;
+ default: /* unhandled request */
+ goto unknown_req;
+ }
+
+ } else {
+ goto unknown_req;
+ }
+
+ return;
+unknown_req:
+ STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_RX_VALID | EP_TX_STALL, 0);
+}
+
+static void ep0_tx(void)
+{
+ if (set_addr) {
+ STM32_USB_DADDR = set_addr | 0x80;
+ set_addr = 0;
+ CPRINTF("SETAD %02x\n", STM32_USB_DADDR);
+ }
+
+ STM32_TOGGLE_EP(0, EP_TX_MASK, EP_TX_VALID, 0);
+}
+
+static void ep0_reset(void)
+{
+ STM32_USB_EP(0) = (1 << 9) /* control EP */ |
+ (2 << 4) /* TX NAK */ |
+ (3 << 12) /* RX VALID */;
+
+ btable_ep[0].tx_addr = usb_sram_addr(ep0_buf_tx);
+ btable_ep[0].rx_addr = usb_sram_addr(ep0_buf_rx);
+ btable_ep[0].rx_count = 0x8000 | ((USB_MAX_PACKET_SIZE/32-1) << 10);
+ btable_ep[0].tx_count = 0;
+}
+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
+ */
+ STM32_USB_DADDR = 0 | 0x80;
+ CPRINTF("RST EP0 %04x\n", STM32_USB_EP(0));
+}
+
+void usb_interrupt(void)
+{
+ uint16_t status = STM32_USB_ISTR;
+
+ if ((status & (1 << 10)))
+ usb_reset();
+
+ if (status & (1 << 15)) {
+ int ep = status & 0x000f;
+ if (ep < USB_EP_COUNT) {
+ if (status & 0x0010)
+ usb_ep_rx[ep]();
+ else
+ usb_ep_tx[ep]();
+ }
+ /* TODO: do it in a USB task */
+ /* task_set_event(, 1 << ep_task); */
+ }
+
+ /* ack interrupts */
+ STM32_USB_ISTR = 0;
+}
+DECLARE_IRQ(STM32_IRQ_USB_LP, usb_interrupt, 1);
+
+static void usb_init(void)
+{
+ /* Enable USB device clock. */
+ STM32_RCC_APB1ENR |= STM32_RCC_PB1_USB;
+
+ /* we need a proper 48MHz clock */
+ clock_enable_module(MODULE_USB, 1);
+
+ /* power on sequence */
+
+ /* keep FRES (USB reset) and remove PDWN (power down) */
+ STM32_USB_CNTR = 0x01;
+ udelay(1); /* startup time */
+ /* reset FRES and keep interrupts masked */
+ STM32_USB_CNTR = 0x00;
+ /* clear pending interrupts */
+ STM32_USB_ISTR = 0;
+
+ /* set descriptors table offset in dedicated SRAM */
+ STM32_USB_BTABLE = 0;
+
+ /* EXTI18 is USB wake up interrupt */
+ /* STM32_EXTI_RTSR |= 1 << 18; */
+ /* STM32_EXTI_IMR |= 1 << 18; */
+
+ /* Enable interrupt handlers */
+ task_enable_irq(STM32_IRQ_USB_LP);
+ /* set interrupts mask : reset/correct tranfer/errors */
+ STM32_USB_CNTR = 0xe400;
+
+ /* set pull-up on DP for FS mode */
+#ifdef CHIP_VARIANT_STM32L15X
+ STM32_SYSCFG_PMC |= 1;
+#elif defined(CHIP_FAMILY_STM32F0)
+ STM32_USB_BCDR |= 1 << 15 /* DPPU */;
+#else
+ /* hardwired or regular GPIO on other platforms */
+#endif
+
+ CPRINTF("USB init done\n");
+}
+DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT);
diff --git a/chip/stm32/usb_endpoints.S b/chip/stm32/usb_endpoints.S
new file mode 100644
index 0000000000..831b4a8dee
--- /dev/null
+++ b/chip/stm32/usb_endpoints.S
@@ -0,0 +1,101 @@
+/* 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
+
+ep_undefined:
+iface_undefined:
+ bx lr
+
+/* 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
diff --git a/common/console_output.c b/common/console_output.c
index 9722adbcb1..99e30cc6ce 100644
--- a/common/console_output.c
+++ b/common/console_output.c
@@ -54,6 +54,7 @@ static const char * const channel_names[] = {
"system",
"task",
"thermal",
+ "usb",
"usbcharge",
"usbpd",
"vboot",
diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S
index e345bc93ca..dcadd43a6a 100644
--- a/core/cortex-m/ec.lds.S
+++ b/core/cortex-m/ec.lds.S
@@ -18,6 +18,9 @@ MEMORY
{
FLASH (rx) : ORIGIN = FW_OFF(SECTION), LENGTH = FW_SIZE(SECTION)
IRAM (rw) : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE
+#ifdef CONFIG_USB
+ USB_RAM (rw) : ORIGIN = CONFIG_USB_RAM_BASE, LENGTH = CONFIG_USB_RAM_SIZE
+#endif
}
SECTIONS
{
@@ -120,6 +123,14 @@ SECTIONS
KEEP(*(.rodata.deferred))
__deferred_funcs_end = .;
+ __usb_desc = .;
+ KEEP(*(.rodata.usb_desc_conf))
+ KEEP(*(SORT(.rodata.usb_desc*)))
+ . = ALIGN(2); /* USB transfers enjoy 16-bit aligned size */
+ __usb_desc_end = .;
+ . = ALIGN(4);
+ KEEP(*(.rodata.usb_ep))
+
. = ALIGN(4);
*(.rodata*)
@@ -208,6 +219,15 @@ SECTIONS
ASSERT(FW_SIZE(SECTION) >
(LOADADDR(.data) + SIZEOF(.data) - FW_OFF(SECTION)),
"No room left in the flash")
+
+#ifdef CONFIG_USB
+ .usb_ram (NOLOAD) : {
+ __usb_ram_start = .;
+ . = ALIGN(8);
+ *(.usb_ram.btable)
+ *(.usb_ram.data)
+ } > USB_RAM
+#endif
#if !(defined(SECTION_IS_RO) && defined(CONFIG_FLASH))
/DISCARD/ : {
*(.google)
diff --git a/core/cortex-m0/ec.lds.S b/core/cortex-m0/ec.lds.S
index 28e52869f8..beb819afa6 100644
--- a/core/cortex-m0/ec.lds.S
+++ b/core/cortex-m0/ec.lds.S
@@ -18,6 +18,9 @@ MEMORY
{
FLASH (rx) : ORIGIN = FW_OFF(SECTION), LENGTH = FW_SIZE(SECTION)
IRAM (rw) : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE
+#ifdef CONFIG_USB
+ USB_RAM (rw) : ORIGIN = CONFIG_USB_RAM_BASE, LENGTH = CONFIG_USB_RAM_SIZE
+#endif
}
SECTIONS
{
@@ -120,6 +123,14 @@ SECTIONS
KEEP(*(.rodata.deferred))
__deferred_funcs_end = .;
+ __usb_desc = .;
+ KEEP(*(.rodata.usb_desc_conf))
+ KEEP(*(SORT(.rodata.usb_desc*)))
+ . = ALIGN(2); /* USB transfers enjoy 16-bit aligned size */
+ __usb_desc_end = .;
+ . = ALIGN(4);
+ KEEP(*(.rodata.usb_ep))
+
. = ALIGN(4);
*(.rodata*)
@@ -197,6 +208,15 @@ SECTIONS
ASSERT(FW_SIZE(SECTION) >
(LOADADDR(.data) + SIZEOF(.data) - FW_OFF(SECTION)),
"No room left in the flash")
+
+#ifdef CONFIG_USB
+ .usb_ram (NOLOAD) : {
+ __usb_ram_start = .;
+ . = ALIGN(8);
+ *(.usb_ram.btable)
+ *(.usb_ram.data)
+ } > USB_RAM
+#endif
#if !(defined(SECTION_IS_RO) && defined(CONFIG_FLASH))
/DISCARD/ : {
*(.google)
diff --git a/include/config.h b/include/config.h
index be01bc87ae..d1e41c54ea 100644
--- a/include/config.h
+++ b/include/config.h
@@ -879,6 +879,9 @@
/* USB PD minimum battery charge to negotiate for more power */
#define CONFIG_USB_PD_MIN_BATT_CHARGE 1
+/* Compile chip support for the USB device controller */
+#undef CONFIG_USB
+
/* USB PD transmit uses SPI master */
#undef CONFIG_USB_PD_TX_USES_SPI_MASTER
diff --git a/include/console.h b/include/console.h
index f2113cda00..a7ee2f1b67 100644
--- a/include/console.h
+++ b/include/console.h
@@ -50,6 +50,7 @@ enum console_channel {
CC_SYSTEM,
CC_TASK,
CC_THERMAL,
+ CC_USB,
CC_USBCHARGE,
CC_USBPD,
CC_VBOOT,
diff --git a/include/link_defs.h b/include/link_defs.h
index 3346ea8e5a..0aab55befe 100644
--- a/include/link_defs.h
+++ b/include/link_defs.h
@@ -54,6 +54,11 @@ extern const struct hook_data __hooks_second_end[];
extern const struct deferred_data __deferred_funcs[];
extern const struct deferred_data __deferred_funcs_end[];
+/* USB data */
+extern const uint8_t __usb_desc[];
+extern const uint8_t __usb_desc_end[];
+extern const uint16_t __usb_ram_start[];
+
/* I2C fake devices for unit testing */
extern const struct test_i2c_read_dev __test_i2c_read8[];
extern const struct test_i2c_read_dev __test_i2c_read8_end[];
diff --git a/include/module_id.h b/include/module_id.h
index f3fbd188e8..23253c31f3 100644
--- a/include/module_id.h
+++ b/include/module_id.h
@@ -40,6 +40,7 @@ enum module_id {
MODULE_TASK,
MODULE_THERMAL,
MODULE_UART,
+ MODULE_USB,
MODULE_USB_PD,
MODULE_USB_PORT_POWER,
MODULE_USB_SWITCH,
diff --git a/include/usb.h b/include/usb.h
new file mode 100644
index 0000000000..618da651e6
--- /dev/null
+++ b/include/usb.h
@@ -0,0 +1,245 @@
+/* Copyright (c) 2013 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 definitions.
+ */
+
+#ifndef USB_H
+#define USB_H
+
+#include <stddef.h> /* for wchar_t */
+
+#define USB_MAX_PACKET_SIZE 64
+
+/* USB 2.0 chapter 9 definitions */
+
+/* Descriptor types */
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIGURATION 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+#define USB_DT_DEVICE_QUALIFIER 0x06
+#define USB_DT_OTHER_SPEED_CONFIG 0x07
+#define USB_DT_INTERFACE_POWER 0x08
+#define USB_DT_DEBUG 0x0a
+
+/* USB Device Descriptor */
+struct usb_device_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+} __packed;
+#define USB_DT_DEVICE_SIZE 18
+
+/* Configuration Descriptor */
+struct usb_config_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wTotalLength;
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+} __packed;
+#define USB_DT_CONFIG_SIZE 9
+
+/* String Descriptor */
+struct usb_string_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wData[1];
+} __packed;
+
+/* Interface Descriptor */
+struct usb_interface_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+} __packed;
+#define USB_DT_INTERFACE_SIZE 9
+
+/* Endpoint Descriptor */
+struct usb_endpoint_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+} __packed;
+#define USB_DT_ENDPOINT_SIZE 7
+
+/* USB Class codes */
+#define USB_CLASS_PER_INTERFACE 0x00
+#define USB_CLASS_AUDIO 0x01
+#define USB_CLASS_COMM 0x02
+#define USB_CLASS_HID 0x03
+#define USB_CLASS_PHYSICAL 0x05
+#define USB_CLASS_STILL_IMAGE 0x06
+#define USB_CLASS_PRINTER 0x07
+#define USB_CLASS_MASS_STORAGE 0x08
+#define USB_CLASS_HUB 0x09
+#define USB_CLASS_CDC_DATA 0x0a
+#define USB_CLASS_CSCID 0x0b
+#define USB_CLASS_CONTENT_SEC 0x0d
+#define USB_CLASS_VIDEO 0x0e
+#define USB_CLASS_WIRELESS_CONTROLLER 0xe0
+#define USB_CLASS_MISC 0xef
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+/* USB Vendor ID assigned to Google Inc. */
+#define USB_VID_GOOGLE 0x18d1
+
+/* Control requests */
+
+/* bRequestType fields */
+/* direction field */
+#define USB_DIR_OUT 0 /* from host to uC */
+#define USB_DIR_IN 0x80 /* from uC to host */
+/* type field */
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+/* recipient field */
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/* Standard requests for bRequest field in a SETUP packet. */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+/* Helpers for descriptors */
+
+#define WIDESTR(quote) WIDESTR2(quote)
+#define WIDESTR2(quote) L##quote
+
+#define USB_STRING_DESC(str) \
+ (const void *)&(const struct { \
+ uint8_t _len; \
+ uint8_t _type; \
+ wchar_t _data[sizeof(str)]; \
+ }) { \
+ /* Total size of the descriptor is : \
+ * size of the UTF-16 text plus the len/type fields \
+ * minus the string 0-termination \
+ */ \
+ sizeof(WIDESTR(str)) + 2 - 2, \
+ USB_DT_STRING, \
+ WIDESTR(str) \
+ }
+
+
+#define USB_CONF_DESC(name) CONCAT2(usb_desc_, name) \
+ __attribute__((section(".rodata.usb_desc_" STRINGIFY(name))))
+
+/* build descriptor names to order them properly */
+#define USB_IFACE_DESC(num) USB_CONF_DESC(CONCAT3(iface, num, _0iface))
+#define USB_EP_DESC(i, num) USB_CONF_DESC(CONCAT4(iface, i, _1ep, num))
+#define USB_CUSTOM_DESC(i, name) USB_CONF_DESC(CONCAT4(iface, i, _2, name))
+
+#define USB_DESC_SIZE (__usb_desc_end - __usb_desc)
+
+/* Helpers for managing the USB controller dedicated RAM */
+
+/* primitive to access the words in USB RAM */
+#ifdef CHIP_FAMILY_STM32F0
+typedef uint16_t usb_uint;
+#else
+/* older chips use a weird addressing were 16-bit words are 32-bit appart */
+typedef uint32_t usb_uint;
+#endif
+
+struct stm32_endpoint {
+ volatile usb_uint tx_addr;
+ volatile usb_uint tx_count;
+ volatile usb_uint rx_addr;
+ volatile usb_uint rx_count;
+};
+
+/* attribute to define a variable in USB RAM */
+#define __usb_ram __attribute__((section(".usb_ram.data")))
+
+extern struct stm32_endpoint btable_ep[];
+
+/* Copy data to the USB dedicated RAM and take care of the weird addressing */
+static inline void memcpy_usbram(usb_uint *ebuf, const uint8_t *src, int size)
+{
+ int i;
+ for (i = 0; i < size / 2; i++, src += 2)
+ *ebuf++ = src[0] | (src[1] << 8);
+ if (size & 1)
+ *ebuf++ = src[0];
+}
+
+/* Compute the address inside dedicate SRAM for the USB controller */
+#define usb_sram_addr(x) \
+ (((uint32_t)(x) - (uint32_t)__usb_ram_start) \
+ / (sizeof(usb_uint)/sizeof(uint16_t)))
+
+/* String descriptors are defined in the board code */
+extern const void * const usb_strings[];
+extern const uint8_t usb_string_desc[];
+
+/* Helpers for endpoint declaration */
+
+#define EP_HANDLER2(num, suffix) CONCAT3(ep_, num, suffix)
+#define EP_TX_HANDLER(num) EP_HANDLER2(num, _tx)
+#define EP_RX_HANDLER(num) EP_HANDLER2(num, _rx)
+#define EP_RESET_HANDLER(num) EP_HANDLER2(num, _rst)
+
+#define USB_DECLARE_EP(num, tx_handler, rx_handler, rst_handler) \
+ void EP_TX_HANDLER(num)(void) \
+ __attribute__ ((alias(STRINGIFY(tx_handler)))); \
+ void EP_RX_HANDLER(num)(void) \
+ __attribute__ ((alias(STRINGIFY(rx_handler)))); \
+ void EP_RESET_HANDLER(num)(void) \
+ __attribute__ ((alias(STRINGIFY(rst_handler))));
+
+/* arrays with all endpoint callbacks */
+extern void (*usb_ep_tx[]) (void);
+extern void (*usb_ep_rx[]) (void);
+extern void (*usb_ep_reset[]) (void);
+/* array with interface-specific control request callbacks */
+extern void (*usb_iface_request[]) (usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx);
+
+#define IFACE_HANDLER(num) CONCAT3(iface_, num, _request)
+#define USB_DECLARE_IFACE(num, handler) \
+ void IFACE_HANDLER(num)(void) \
+ __attribute__ ((alias(STRINGIFY(handler))));
+
+#endif /* USB_H */