diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2014-06-09 15:03:35 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-10-11 02:22:50 +0000 |
commit | b50e5a8b3066cdab6cb98f25038ef8649410ce2a (patch) | |
tree | 54d207d5e9f8153f017711f63649e6024ba0174b /board/twinkie | |
parent | 5822d01f99f09443dc81dd9a85143047a0e1a1c7 (diff) | |
download | chrome-ec-b50e5a8b3066cdab6cb98f25038ef8649410ce2a.tar.gz |
twinkie: add config to behave as a PD message injector
Add a compile-time configuration to make Twinkie behave as
a USB Power Delivery consumer/provider device rather than a
transparent sniffer.
To use it, edit board/twinkie/ec.tasklist:
enable the PD task and comment out the SNIFFER task.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BRANCH=none
BUG=none
TEST=connect it to a Fruitpie and use "pd charger" command on Twinkie
console, see the Fruitpie receiving the source capabilities and
answering.
Change-Id: Ic2b4ba2d166ea1d3f5f9be410453a030a4ee7b68
Reviewed-on: https://chromium-review.googlesource.com/204168
Reviewed-by: Todd Broch <tbroch@chromium.org>
Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
Tested-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'board/twinkie')
-rw-r--r-- | board/twinkie/board.h | 16 | ||||
-rw-r--r-- | board/twinkie/build.mk | 4 | ||||
-rw-r--r-- | board/twinkie/ec.tasklist | 8 | ||||
-rw-r--r-- | board/twinkie/gpio.inc | 4 | ||||
-rw-r--r-- | board/twinkie/sniffer.c | 2 | ||||
-rw-r--r-- | board/twinkie/usb_pd_config.h | 188 | ||||
-rw-r--r-- | board/twinkie/usb_pd_policy.c | 123 |
7 files changed, 338 insertions, 7 deletions
diff --git a/board/twinkie/board.h b/board/twinkie/board.h index 51abd57353..6e4dc5c1ee 100644 --- a/board/twinkie/board.h +++ b/board/twinkie/board.h @@ -19,6 +19,16 @@ #define CONFIG_USB #define CONFIG_USB_CONSOLE +#ifdef HAS_TASK_PD /* PD message injector mode */ +#define CONFIG_USB_POWER_DELIVERY +#define CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_INTERNAL_COMP +#define CONFIG_PD_USE_DAC_AS_REF +#define CONFIG_HW_CRC +#else /* PD sniffer mode */ +#undef CONFIG_DMA_DEFAULT_HANDLERS +#endif + #define CONFIG_ADC #define CONFIG_BOARD_PRE_INIT #define CONFIG_I2C @@ -26,7 +36,6 @@ #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 @@ -45,7 +54,6 @@ #ifndef __ASSEMBLER__ /* Timer selection */ -#define TIM_CLOCK_PD_RX 1 #define TIM_CLOCK32 2 #define TIM_ADC 3 @@ -81,7 +89,11 @@ enum usb_strings { #define USB_EP_CONTROL 0 #define USB_EP_CON_TX 1 #define USB_EP_CON_RX 2 +#ifdef HAS_TASK_SNIFFER #define USB_EP_SNIFFER 3 #define USB_EP_COUNT 4 +#else +#define USB_EP_COUNT 3 +#endif #endif /* __BOARD_H */ diff --git a/board/twinkie/build.mk b/board/twinkie/build.mk index 4171d4b08b..d920169312 100644 --- a/board/twinkie/build.mk +++ b/board/twinkie/build.mk @@ -10,4 +10,6 @@ CHIP:=stm32 CHIP_FAMILY:=stm32f0 CHIP_VARIANT:=stm32f07x -board-y=board.o sniffer.o +board-y=board.o +board-$(HAS_TASK_SNIFFER)+=sniffer.o +board-$(HAS_TASK_PD)+=usb_pd_policy.o diff --git a/board/twinkie/ec.tasklist b/board/twinkie/ec.tasklist index c6ea5044b8..a766187877 100644 --- a/board/twinkie/ec.tasklist +++ b/board/twinkie/ec.tasklist @@ -19,4 +19,10 @@ #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) + TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \ + /*Disabled: TASK_ALWAYS(PD, pd_task, NULL, LARGER_TASK_STACK_SIZE) */ +/* + * To get Twinkie to behave as a USB Power Delivery consumer/provider + * device rather than a transparent sniffer : + * enable the PD task and comment out the SNIFFER task. + */ diff --git a/board/twinkie/gpio.inc b/board/twinkie/gpio.inc index cfb565c753..181603148c 100644 --- a/board/twinkie/gpio.inc +++ b/board/twinkie/gpio.inc @@ -4,8 +4,8 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -GPIO(CC2_ALERT_L, A, 7, GPIO_INT_FALLING, cc2_event) -GPIO(VBUS_ALERT_L, B, 2, GPIO_INT_FALLING, vbus_event) +GPIO(CC2_ALERT_L, A, 7, GPIO_INT_BOTH | GPIO_PULL_UP, cc2_event) +GPIO(VBUS_ALERT_L, B, 2, GPIO_INT_BOTH | GPIO_PULL_UP, vbus_event) GPIO(CC1_EN, A, 0, GPIO_OUT_HIGH, NULL) GPIO(CC1_PD, A, 1, GPIO_ANALOG, NULL) diff --git a/board/twinkie/sniffer.c b/board/twinkie/sniffer.c index b9240e5072..5d6fdcc9e0 100644 --- a/board/twinkie/sniffer.c +++ b/board/twinkie/sniffer.c @@ -100,7 +100,7 @@ USB_DECLARE_EP(USB_EP_SNIFFER, ep_tx, ep_tx, ep_reset); #define EXTI_COMP 21 /* Timer used for RX clocking */ -#define TIM_RX TIM_CLOCK_PD_RX +#define TIM_RX 1 /* Clock divider for RX edges timings (2.4Mhz counter from 48Mhz clock) */ #define RX_CLOCK_DIV (20 - 1) diff --git a/board/twinkie/usb_pd_config.h b/board/twinkie/usb_pd_config.h new file mode 100644 index 0000000000..2f8ab797fe --- /dev/null +++ b/board/twinkie/usb_pd_config.h @@ -0,0 +1,188 @@ +/* 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 Power delivery board configuration */ + +#ifndef __USB_PD_CONFIG_H +#define __USB_PD_CONFIG_H + +#include "ina231.h" + +/* Port and task configuration */ +#define PD_PORT_COUNT 1 +#define PORT_TO_TASK_ID(port) TASK_ID_PD +#define TASK_ID_TO_PORT(id) 0 + +/* Timer selection for baseband PD communication */ +#define TIM_CLOCK_PD_TX_C0 17 +#define TIM_CLOCK_PD_RX_C0 1 + +#define TIM_CLOCK_PD_TX(p) TIM_CLOCK_PD_TX_C0 +#define TIM_CLOCK_PD_RX(p) TIM_CLOCK_PD_RX_C0 + +/* TX and RX timer register */ +#define TIM_REG_TX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_TX_C0)) +#define TIM_REG_RX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_RX_C0)) +#define TIM_REG_TX(p) TIM_REG_TX_C0 +#define TIM_REG_RX(p) TIM_REG_RX_C0 + +/* Timer channel */ +#define TIM_RX_CCR_C0 1 + +/* RX timer capture/compare register */ +#define TIM_CCR_C0 (&STM32_TIM_CCRx(TIM_CLOCK_PD_RX_C0, TIM_RX_CCR_C0)) +#define TIM_RX_CCR_REG(p) TIM_CCR_C0 + +/* use the hardware accelerator for CRC */ +#define CONFIG_HW_CRC + +/* TX is using SPI1 on PA6/PB4 */ +#define SPI_REGS(p) STM32_SPI1_REGS +#define DMAC_SPI_TX(p) STM32_DMAC_CH3 + +static inline void spi_enable_clock(int port) +{ + STM32_RCC_APB2ENR |= STM32_RCC_PB2_SPI1; +} + +/* RX is using COMP1 triggering TIM1 CH1 */ +#define CMP1OUTSEL STM32_COMP_CMP1OUTSEL_TIM1_IC1 +#define CMP2OUTSEL 0 + +#define DMAC_TIM_RX(p) STM32_DMAC_CH2 +#define TIM_CCR_IDX(p) TIM_RX_CCR_C0 +#define TIM_CCR_CS 1 +#define EXTI_COMP_MASK(p) ((1 << 21) | (1 << 22)) +#define IRQ_COMP STM32_IRQ_COMP +/* triggers packet detection on comparator falling edge */ +#define EXTI_XTSR STM32_EXTI_FTSR + +/* the pins used for communication need to be hi-speed */ +static inline void pd_set_pins_speed(int port) +{ + /* 40 MHz pin speed on SPI TX PB4 */ + STM32_GPIO_OSPEEDR(GPIO_B) |= 0x00000300; + /* 40 MHz pin speed on SPI TX PA6 */ + STM32_GPIO_OSPEEDR(GPIO_A) |= 0x00003000; + /* 40 MHz pin speed on TIM17_CH1 (PB9) */ + STM32_GPIO_OSPEEDR(GPIO_B) |= 0x000C0000; +} + +/* Reset SPI peripheral used for TX */ +static inline void pd_tx_spi_reset(int port) +{ + /* Reset SPI1 */ + STM32_RCC_APB2RSTR |= (1 << 12); + STM32_RCC_APB2RSTR &= ~(1 << 12); +} + +/* Drive the CC line from the TX block */ +static inline void pd_tx_enable(int port, int polarity) +{ +#if 0 /* Transmit only on the active CC line */ + if (polarity) { + gpio_set_level(GPIO_CC2_TX_EN, 1); + /* TX_DATA on PA6 is now connected to SPI1 */ + gpio_set_alternate_function(GPIO_A, 0x0040, 0); + } else { + gpio_set_level(GPIO_CC1_TX_EN, 1); + /* TX_DATA on PB4 is now connected to SPI1 */ + gpio_set_alternate_function(GPIO_B, 0x0010, 0); + } +#else /* Transmit on both CC lines */ + gpio_set_level(GPIO_CC2_TX_EN, 1); + gpio_set_level(GPIO_CC1_TX_EN, 1); + /* TX_DATA on PA6 is now connected to SPI1 */ + gpio_set_alternate_function(GPIO_A, 0x0040, 0); + /* TX_DATA on PB4 is now connected to SPI1 */ + gpio_set_alternate_function(GPIO_B, 0x0010, 0); +#endif +} + +/* Put the TX driver in Hi-Z state */ +static inline void pd_tx_disable(int port, int polarity) +{ + /* TX_DATA on PB4 is an output low GPIO to disable the FET */ + STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B) & ~(3 << (2*4))) + | (1 << (2*4)); + /* TX_DATA on PA6 is an output low GPIO to disable the FET */ + STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A) & ~(3 << (2*6))) + | (1 << (2*6)); + /* + * Tri-state the low side after the high side + * to ensure we are not going above Vnc + */ + gpio_set_level(GPIO_CC1_TX_EN, 0); + gpio_set_level(GPIO_CC2_TX_EN, 0); +} + +/* we know the plug polarity, do the right configuration */ +static inline void pd_select_polarity(int port, int polarity) +{ + /* use the right comparator */ + STM32_COMP_CSR = (STM32_COMP_CSR + & ~(STM32_COMP_CMP1INSEL_MASK | STM32_COMP_CMP2INSEL_MASK + |STM32_COMP_CMP1EN | STM32_COMP_CMP2EN)) + | STM32_COMP_CMP1INSEL_INM4 | STM32_COMP_CMP2INSEL_INM4 + | (polarity ? STM32_COMP_CMP2EN : STM32_COMP_CMP1EN); +} + +/* Initialize pins used for clocking */ +static inline void pd_tx_init(void) +{ + gpio_config_module(MODULE_USB_PD, 1); + + /* Detect when VBUS crosses the 4.5V threshold (1.25mV/bit) */ + ina231_write(0, INA231_REG_ALERT, 4500 * 100 / 125); + ina231_write(0, INA231_REG_MASK, INA231_MASK_EN_BOL); + /* start as a power consumer */ + gpio_set_level(GPIO_CC1_RD, 0); + gpio_set_level(GPIO_CC2_RD, 0); +} + +static inline void pd_set_host_mode(int port, int enable) +{ + if (enable) { + gpio_set_level(GPIO_CC1_RD, 1); + gpio_set_level(GPIO_CC2_RD, 1); + /* set Rp by driving high RPUSB GPIO */ + gpio_set_flags(GPIO_CC1_RPUSB, GPIO_OUT_HIGH); + gpio_set_flags(GPIO_CC2_RPUSB, GPIO_OUT_HIGH); + } else { + /* put back RPUSB GPIO in the default state and set Rd */ + gpio_set_flags(GPIO_CC1_RPUSB, GPIO_ODR_HIGH); + gpio_set_flags(GPIO_CC2_RPUSB, GPIO_ODR_HIGH); + gpio_set_level(GPIO_CC1_RD, 0); + gpio_set_level(GPIO_CC2_RD, 0); + } +} + +static inline int pd_adc_read(int port, int cc) +{ + if (cc == 0) + return adc_read_channel(ADC_CH_CC1_PD); + else + return adc_read_channel(ADC_CH_CC2_PD); +} + +static inline int pd_snk_is_vbus_provided(int port) +{ + /* assume the alert was programmed to detect bus voltage above 4.5V */ + return (gpio_get_level(GPIO_VBUS_ALERT_L) == 0); +} + +/* Standard-current DFP : no-connect voltage is 1.55V */ +#define PD_SRC_VNC 1550 /* mV */ + +/* UFP-side : threshold for DFP connection detection */ +#define PD_SNK_VA 200 /* mV */ + +/* start as a sink in case we have no other power supply/battery */ +#define PD_DEFAULT_STATE PD_STATE_SNK_DISCONNECTED + +/* delay necessary for the voltage transition on the power supply */ +#define PD_POWER_SUPPLY_TRANSITION_DELAY 50000 /* us */ + +#endif /* __USB_PD_CONFIG_H */ diff --git a/board/twinkie/usb_pd_policy.c b/board/twinkie/usb_pd_policy.c new file mode 100644 index 0000000000..a67c4bc2d6 --- /dev/null +++ b/board/twinkie/usb_pd_policy.c @@ -0,0 +1,123 @@ +/* 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 "hooks.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "usb_pd.h" + +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + +const uint32_t pd_src_pdo[] = { + PDO_FIXED(5000, 500, PDO_FIXED_EXTERNAL), + PDO_FIXED(5000, 3000, 0), + PDO_FIXED(12000, 3000, 0), + PDO_FIXED(20000, 2000, 0), +}; +const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); + +const uint32_t pd_snk_pdo[] = { + PDO_BATT(4500, 5500, 15000), + PDO_BATT(11500, 12500, 36000), +}; +const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); + +/* Cap on the max voltage requested as a sink (in millivolts) */ +static unsigned max_mv = -1; /* no cap */ + +int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo) +{ + int i; + int sel_mv; + int max_uw = 0; + int max_i = -1; + + /* Get max power */ + for (i = 0; i < cnt; i++) { + int uw; + int mv = ((src_caps[i] >> 10) & 0x3FF) * 50; + if ((src_caps[i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { + uw = 250000 * (src_caps[i] & 0x3FF); + } else { + int ma = (src_caps[i] & 0x3FF) * 10; + uw = ma * mv; + } + if ((uw > max_uw) && (mv <= max_mv)) { + max_i = i; + max_uw = uw; + sel_mv = mv; + } + } + if (max_i < 0) + return -EC_ERROR_UNKNOWN; + + /* request all the power ... */ + if ((src_caps[max_i] & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) { + int uw = 250000 * (src_caps[i] & 0x3FF); + *rdo = RDO_BATT(max_i + 1, uw/2, uw, 0); + ccprintf("Request [%d] %dV %d/%d mW\n", + max_i, sel_mv/1000, uw/1000, uw/1000); + } else { + int ma = 10 * (src_caps[max_i] & 0x3FF); + *rdo = RDO_FIXED(max_i + 1, ma / 2, ma, 0); + ccprintf("Request [%d] %dV %d/%d mA\n", + max_i, sel_mv/1000, max_i, ma/2, ma); + } + return EC_SUCCESS; +} + +void pd_set_input_current_limit(uint32_t max_ma) +{ +} + +void pd_set_max_voltage(unsigned mv) +{ + max_mv = mv; +} + +int pd_request_voltage(uint32_t rdo) +{ + int op_ma = rdo & 0x3FF; + int max_ma = (rdo >> 10) & 0x3FF; + int idx = rdo >> 28; + uint32_t pdo; + uint32_t pdo_ma; + + if (!idx || idx > pd_src_pdo_cnt) + return EC_ERROR_INVAL; /* Invalid index */ + + /* check current ... */ + pdo = pd_src_pdo[idx - 1]; + pdo_ma = (pdo & 0x3ff); + if (op_ma > pdo_ma) + return EC_ERROR_INVAL; /* too much op current */ + if (max_ma > pdo_ma) + return EC_ERROR_INVAL; /* too much max current */ + + ccprintf("Switch to %d V %d mA (for %d/%d mA)\n", + ((pdo >> 10) & 0x3ff) * 50, (pdo & 0x3ff) * 10, + ((rdo >> 10) & 0x3ff) * 10, (rdo & 0x3ff) * 10); + + return EC_SUCCESS; +} + +int pd_set_power_supply_ready(int port) +{ + return EC_SUCCESS; /* we are ready */ +} + +void pd_power_supply_reset(int port) +{ +} + +int pd_board_checks(void) +{ + return EC_SUCCESS; +} |