diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2013-06-05 17:15:05 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-06-18 06:08:45 +0000 |
commit | 5fa52a895b6acd5d976a24dd571a44eaa3cb1ad1 (patch) | |
tree | 667ccc50249a31eec51199811b9fd3780ba52729 | |
parent | 4957dcbd87cd75d91bca772fef93ec2e0256ea4f (diff) | |
download | chrome-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.toolchain | 2 | ||||
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/config_chip.h | 4 | ||||
-rw-r--r-- | chip/stm32/registers.h | 40 | ||||
-rw-r--r-- | chip/stm32/usb.c | 263 | ||||
-rw-r--r-- | chip/stm32/usb_endpoints.S | 101 | ||||
-rw-r--r-- | common/console_output.c | 1 | ||||
-rw-r--r-- | core/cortex-m/ec.lds.S | 20 | ||||
-rw-r--r-- | core/cortex-m0/ec.lds.S | 20 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/console.h | 1 | ||||
-rw-r--r-- | include/link_defs.h | 5 | ||||
-rw-r--r-- | include/module_id.h | 1 | ||||
-rw-r--r-- | include/usb.h | 245 |
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 */ |