From 458d39c1a88013227148db86dfc9a9edf67e9453 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Thu, 29 May 2014 20:51:34 -0700 Subject: twinkie: add USB PD sniffer Record the edges on the CC lines to sniff the USB PD traffic. Signed-off-by: Vincent Palatin BRANCH=none BUG=chrome-os-partner:28337 TEST=make BOARD=twinkie Change-Id: I05c3135e47d0dc848875cbc99e4b57aff52ccbf6 Reviewed-on: https://chromium-review.googlesource.com/202206 Tested-by: Vincent Palatin Reviewed-by: Alec Berg Reviewed-by: Todd Broch Tested-by: Todd Broch Commit-Queue: Vincent Palatin --- board/twinkie/board.c | 10 ++ board/twinkie/board.h | 33 +++++++ board/twinkie/build.mk | 2 +- board/twinkie/ec.tasklist | 1 + board/twinkie/sniffer.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++ chip/stm32/registers.h | 2 + 6 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 board/twinkie/sniffer.c diff --git a/board/twinkie/board.c b/board/twinkie/board.c index 669533465b..07faadd11b 100644 --- a/board/twinkie/board.c +++ b/board/twinkie/board.c @@ -14,6 +14,7 @@ #include "ina231.h" #include "registers.h" #include "task.h" +#include "usb.h" #include "util.h" void cc2_event(enum gpio_signal signal) @@ -67,3 +68,12 @@ const struct i2c_port_t i2c_ports[] = { {"master", I2C_PORT_MASTER, 100, GPIO_I2C_SCL, GPIO_I2C_SDA}, }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); + +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("Twinkie"), + [USB_STR_VERSION] = USB_STRING_DESC("v0.001"), + [USB_STR_SNIFFER] = USB_STRING_DESC("USB-PD Sniffer"), +}; +BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); diff --git a/board/twinkie/board.h b/board/twinkie/board.h index 69de431b45..51abd57353 100644 --- a/board/twinkie/board.h +++ b/board/twinkie/board.h @@ -16,6 +16,9 @@ /* Optional features */ #define CONFIG_STM_HWTIMER32 +#define CONFIG_USB +#define CONFIG_USB_CONSOLE + #define CONFIG_ADC #define CONFIG_BOARD_PRE_INIT #define CONFIG_I2C @@ -23,10 +26,16 @@ #undef CONFIG_WATCHDOG_HELP #undef CONFIG_LID_SWITCH #undef CONFIG_TASK_PROFILING +#undef CONFIG_DMA_DEFAULT_HANDLERS /* I2C ports configuration */ #define I2C_PORT_MASTER 0 +/* USB configuration */ +#define CONFIG_USB_PID 0x500A +/* By default, enable all console messages excepted USB */ +#define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_USB)) + /* * Allow dangerous commands all the time, since we don't have a write protect * switch. @@ -49,6 +58,30 @@ enum adc_channel { /* Number of ADC channels */ ADC_CH_COUNT }; + +/* USB string indexes */ +enum usb_strings { + USB_STR_DESC = 0, + USB_STR_VENDOR, + USB_STR_PRODUCT, + USB_STR_VERSION, + USB_STR_SNIFFER, + + USB_STR_COUNT +}; + #endif /* !__ASSEMBLER__ */ +/* USB interface indexes (use define rather than enum to expand them) */ +#define USB_IFACE_CONSOLE 0 +#define USB_IFACE_VENDOR 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_CON_TX 1 +#define USB_EP_CON_RX 2 +#define USB_EP_SNIFFER 3 +#define USB_EP_COUNT 4 + #endif /* __BOARD_H */ diff --git a/board/twinkie/build.mk b/board/twinkie/build.mk index 83b8716734..4171d4b08b 100644 --- a/board/twinkie/build.mk +++ b/board/twinkie/build.mk @@ -10,4 +10,4 @@ CHIP:=stm32 CHIP_FAMILY:=stm32f0 CHIP_VARIANT:=stm32f07x -board-y=board.o +board-y=board.o sniffer.o diff --git a/board/twinkie/ec.tasklist b/board/twinkie/ec.tasklist index 8ff4e8234b..c6ea5044b8 100644 --- a/board/twinkie/ec.tasklist +++ b/board/twinkie/ec.tasklist @@ -18,4 +18,5 @@ */ #define CONFIG_TASK_LIST \ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(SNIFFER, sniffer_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) diff --git a/board/twinkie/sniffer.c b/board/twinkie/sniffer.c new file mode 100644 index 0000000000..b9240e5072 --- /dev/null +++ b/board/twinkie/sniffer.c @@ -0,0 +1,239 @@ +/* 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 "console.h" +#include "dma.h" +#include "gpio.h" +#include "hwtimer.h" +#include "hooks.h" +#include "link_defs.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "usb.h" +#include "util.h" + +/* Size of one USB packet buffer */ +#define EP_BUF_SIZE 64 +/* Size of the payload (packet minus the header) */ +#define EP_PAYLOAD_SIZE (EP_BUF_SIZE - 4) + +/* Buffer enough to avoid overflowing due to USB latencies on both sides */ +#define RX_COUNT (8 * EP_PAYLOAD_SIZE) + +/* Task event for the USB transfer interrupt */ +#define USB_EVENTS TASK_EVENT_CUSTOM(3) + +/* edge timing samples */ +static uint8_t samples[RX_COUNT]; + +/* Bulk endpoint double buffer */ +static usb_uint ep_buf[2][EP_BUF_SIZE / 2] __usb_ram; + +/* The host is reading the incoming packets */ +static int tx_on; + +/* USB descriptors */ +const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_VENDOR) = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_IFACE_VENDOR, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, + .iInterface = USB_STR_SNIFFER, +}; +const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_VENDOR, + USB_EP_SNIFFER) = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x80 | USB_EP_SNIFFER, + .bmAttributes = 0x02 /* Bulk IN */, + .wMaxPacketSize = USB_MAX_PACKET_SIZE, + .bInterval = 1 +}; + +/* USB callbacks */ +static void ep_tx(void) +{ + int b = !(STM32_USB_EP(USB_EP_SNIFFER) & EP_TX_DTOG); + if (b) + btable_ep[USB_EP_SNIFFER].rx_count = 0; + else + btable_ep[USB_EP_SNIFFER].tx_count = 0; + /* wake up the processing */ + task_set_event(TASK_ID_SNIFFER, 1 << b, 0); + /* clear IT, toggle and validate buffers */ + STM32_TOGGLE_EP(USB_EP_SNIFFER, 0, 0, EP_RX_DTOG); + STM32_TOGGLE_EP(USB_EP_SNIFFER, EP_TX_MASK, EP_TX_VALID, 0); + /* record the valid transmission */ + tx_on = 1; +} + +static void ep_reset(void) +{ + /* Bulk IN endpoint with double-buffering */ + btable_ep[USB_EP_SNIFFER].tx_addr = usb_sram_addr(ep_buf[0]); + btable_ep[USB_EP_SNIFFER].rx_addr = usb_sram_addr(ep_buf[1]); + btable_ep[USB_EP_SNIFFER].tx_count = 0; + btable_ep[USB_EP_SNIFFER].rx_count = 0; + STM32_USB_EP(USB_EP_SNIFFER) = (USB_EP_SNIFFER << 0) /*Endpoint Num*/ | + (3 << 4) /* TX Valid */ | + (0 << 9) /* Bulk EP */ | + (1 << 8) /* DBL BUF */ | + (0 << 12) /* RX Disabled */; +} +USB_DECLARE_EP(USB_EP_SNIFFER, ep_tx, ep_tx, ep_reset); + + +/* --- RX operation using comparator linked to timer --- */ +/* RX is using COMP1 triggering TIM1 CH1 */ +#define DMAC_TIM_RX STM32_DMAC_CH6 +#define TIM_CCR_IDX 1 +#define TIM_CCR_CS 1 +#define EXTI_COMP 21 + +/* Timer used for RX clocking */ +#define TIM_RX TIM_CLOCK_PD_RX +/* Clock divider for RX edges timings (2.4Mhz counter from 48Mhz clock) */ +#define RX_CLOCK_DIV (20 - 1) + +static const struct dma_option dma_tim_option = { + DMAC_TIM_RX, (void *)&STM32_TIM_CCRx(TIM_RX, TIM_CCR_IDX), + STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT | + STM32_DMA_CCR_CIRC | STM32_DMA_CCR_TCIE | STM32_DMA_CCR_HTIE +}; + +/* sequence number for sample buffers */ +static volatile uint32_t seq; +/* Buffer overflow count */ +static uint32_t oflow; + +void tim_dma_handler(void) +{ + stm32_dma_regs_t *dma = STM32_DMA1_REGS; + uint32_t stat = dma->isr & (STM32_DMA_ISR_HTIF(DMAC_TIM_RX) + | STM32_DMA_ISR_TCIF(DMAC_TIM_RX)); + seq++; + dma->ifcr |= STM32_DMA_ISR_ALL(DMAC_TIM_RX); + if (tx_on) + task_set_event(TASK_ID_SNIFFER, TASK_EVENT_CUSTOM(stat), 0); +} +DECLARE_IRQ(STM32_IRQ_DMA_CHANNEL_4_7, tim_dma_handler, 1); + +static void sniffer_init(void) +{ + /* remap TIM1 CH1/2/3 to DMA channel 6 */ + STM32_SYSCFG_CFGR1 |= 1 << 28; + /* --- set counter for RX timing : 2.4Mhz rate, free-running --- */ + __hw_timer_enable_clock(TIM_RX, 1); + /* Timer configuration */ + STM32_TIM_CR1(TIM_RX) = 0x0000; + STM32_TIM_CR2(TIM_RX) = 0x0000; + /* Auto-reload value : 8-bit free running counter */ + STM32_TIM_ARR(TIM_RX) = 0xFF; + /* Counter reloading event after 106us */ + STM32_TIM_CCR2(TIM_RX) = 0xFF; + /* Timer ICx input configuration */ +#if TIM_CCR_IDX == 1 + STM32_TIM_CCMR1(TIM_RX) = TIM_CCR_CS << 0; +#elif TIM_CCR_IDX == 4 + STM32_TIM_CCMR2(TIM_RX) = TIM_CCR_CS << 8; +#else +#error Unsupported RX timer capture input +#endif + STM32_TIM_CCER(TIM_RX) = 0xB << ((TIM_CCR_IDX - 1) * 4); + /* TODO: add input filtering */ + /* configure DMA request on CCRx update */ + STM32_TIM_DIER(TIM_RX) = (1 << (8 + TIM_CCR_IDX)) | (1 << (8+2)); + /* set prescaler to /26 (F=2.4Mhz, T=0.4us) */ + STM32_TIM_PSC(TIM_RX) = RX_CLOCK_DIV; + /* Reload the pre-scaler and reset the counter */ + STM32_TIM_EGR(TIM_RX) = 0x0001 | (1 << TIM_CCR_IDX) /* clear CCRx */; + /* clear update event from reloading */ + STM32_TIM_SR(TIM_RX) = 0; + + /* --- DAC configuration for comparator at 550mV --- */ + /* Enable DAC interface clock. */ + STM32_RCC_APB1ENR |= (1 << 29); + /* set voltage Vout=0.550V (Vref = 3.0V) */ + STM32_DAC_DHR12RD = 550 * 4096 / 3000; + /* Start DAC channel 1 */ + STM32_DAC_CR = STM32_DAC_CR_EN1 | STM32_DAC_CR_BOFF1; + + /* --- COMP2 as comparator for RX vs Vmid = 550mV --- */ + /* turn on COMP/SYSCFG */ + STM32_RCC_APB2ENR |= 1 << 0; + /* currently in hi-speed mode : INP = PA1 , INM = DAC1 / PA4 / INM4 */ + STM32_COMP_CSR = STM32_COMP_CMP1EN | STM32_COMP_CMP1MODE_HSPEED | + STM32_COMP_CMP1INSEL_VREF12 | + /*STM32_COMP_CMP1INSEL_INM4 |*/ + STM32_COMP_CMP1OUTSEL_TIM1_IC1 | + STM32_COMP_CMP1HYST_HI; + ccprintf("Sniffer initialized\n"); + + + /* start sampling the edges on the CC line using the RX timer */ + dma_start_rx(&dma_tim_option, RX_COUNT, samples); + task_enable_irq(STM32_IRQ_DMA_CHANNEL_4_7); + /* start RX timer */ + STM32_TIM_CR1(TIM_RX) |= 1; +} +DECLARE_HOOK(HOOK_INIT, sniffer_init, HOOK_PRIO_DEFAULT); + +/* Task to post-process the samples and copy them the USB endpoint buffer */ +void sniffer_task(void) +{ + uint16_t cnt = 0; + int off = 0; + uint8_t freebuf = 3; + struct stm32_endpoint *ep = btable_ep+USB_EP_SNIFFER; + + while (1) { + /* Wait for a new buffer of samples or a new USB free buffer */ + uint32_t evt = task_wait_event(-1); + + freebuf |= evt & USB_EVENTS; + if (evt & ~USB_EVENTS) { + off = evt & STM32_DMA_ISR_TCIF(DMAC_TIM_RX) ? + RX_COUNT/2 : 0; + cnt += RX_COUNT/2; + if (cnt > RX_COUNT) { + cnt -= RX_COUNT/2; + oflow++; + } + } + /* send the available samples over USB if we have a buffer*/ + while (cnt && freebuf) { + int b = freebuf & 1 ? 0 : 1; + freebuf &= ~(1 << b); + ep_buf[b][0] = (seq << 8) | 1 /* CC1 stream */; + ep_buf[b][1] = __hw_clock_source_read(); + memcpy_usbram(ep_buf[b] + 2, + samples+off, EP_PAYLOAD_SIZE); + if (b) + ep->rx_count = EP_BUF_SIZE; + else + ep->tx_count = EP_BUF_SIZE; + cnt -= EP_PAYLOAD_SIZE; + off += EP_PAYLOAD_SIZE; + if (off >= RX_COUNT) + off = 0; + } + } +} + +static int command_sniffer(int argc, char **argv) +{ + ccprintf("Seq number:%d Overflows: %d\n", seq, oflow); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(sniffer, command_sniffer, + "[]", "Buffering status", NULL); diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 3f519e9345..b822660141 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -1274,11 +1274,13 @@ typedef volatile struct stm32_dma_regs stm32_dma_regs_t; #define STM32_USB_BCDR REG16(STM32_USB_FS_BASE + 0x58) #define EP_MASK 0x0F0F +#define EP_TX_DTOG 0x0040 #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_DTOG 0x4000 #define EP_RX_MASK 0x3000 #define EP_RX_VALID 0x3000 #define EP_RX_NAK 0x2000 -- cgit v1.2.1