diff options
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/registers-stm32g4.h | 193 | ||||
-rw-r--r-- | chip/stm32/ucpd-stm32gx.c | 277 | ||||
-rw-r--r-- | chip/stm32/ucpd-stm32gx.h | 111 |
4 files changed, 582 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index a951c0eb21..f2d5720961 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -98,3 +98,4 @@ chip-$(CONFIG_USB_ISOCHRONOUS)+=usb_isochronous.o chip-$(CONFIG_USB_PD_TCPC)+=usb_pd_phy.o chip-$(CONFIG_USB_SPI)+=usb_spi.o endif +chip-$(CONFIG_USB_PD_TCPM_STM32GX)+=ucpd-stm32gx.o diff --git a/chip/stm32/registers-stm32g4.h b/chip/stm32/registers-stm32g4.h index 6e519bc9fb..4fe97e4c29 100644 --- a/chip/stm32/registers-stm32g4.h +++ b/chip/stm32/registers-stm32g4.h @@ -252,6 +252,195 @@ /* Register definitions */ +/* --- UCPD --- */ +#define STM32_UCPD_REG(port, offset) \ + REG32(((STM32_UCPD1_BASE + ((port) * 0x400)) + (offset))) + +#define STM32_UCPD_CFGR1(port) STM32_UCPD_REG(port, 0x00) +#define STM32_UCPD_CFGR2(port) STM32_UCPD_REG(port, 0x04) +#define STM32_UCPD_CR(port) STM32_UCPD_REG(port, 0x0c) +#define STM32_UCPD_IMR(port) STM32_UCPD_REG(port, 0x10) +#define STM32_UCPD_SR(port) STM32_UCPD_REG(port, 0x14) +#define STM32_UCPD_ICR(port) STM32_UCPD_REG(port, 0x18) +#define STM32_UCPD_TX_ORDSETR(port) STM32_UCPD_REG(port, 0x1c) +#define STM32_UCPD_TX_PAYSZR(port) STM32_UCPD_REG(port, 0x20) +#define STM32_UCPD_TXDR(port) STM32_UCPD_REG(port, 0x24) +#define STM32_UCPD_RX_ORDSETR(port) STM32_UCPD_REG(port, 0x28) +#define STM32_UCPD_RX_PAYSZR(port) STM32_UCPD_REG(port, 0x2c) +#define STM32_UCPD_RXDR(port) STM32_UCPD_REG(port, 0x30) +#define STM32_UCPD_RX_ORDEXTR1(port) STM32_UCPD_REG(port, 0x34) +#define STM32_UCPD_RX_ORDEXTR2(port) STM32_UCPD_REG(port, 0x38) + +/* --- UCPD CFGR1 Bit Definitions --- */ +#define STM32_UCPD_CFGR1_HBITCLKD_SHIFT 0 +#define STM32_UCPD_CFGR1_HBITCLKD_MASK ((0x3f) << \ + (STM32_UCPD_CFGR1_HBITCLKD_SHIFT)) +#define STM32_UCPD_CFGR1_HBITCLKD_VAL(x) ((x) << \ + STM32_UCPD_CFGR1_HBITCLKD_SHIFT) +#define STM32_UCPD_CFGR1_IFRGAP_SHIFT 6 +#define STM32_UCPD_CFGR1_IFRGAP_MASK ((0x1f) << \ + (STM32_UCPD_CFGR1_IFRGAP_SHIFT)) +#define STM32_UCPD_CFGR1_IFRGAP_VAL(x) ((x) << \ + STM32_UCPD_CFGR1_IFRGAP_SHIFT) +#define STM32_UCPD_CFGR1_TRANSWIN_SHIFT 11 +#define STM32_UCPD_CFGR1_TRANSWIN_MASK ((0x1f) << \ + (STM32_UCPD_CFGR1_TRANSWIN_SHIFT)) +#define STM32_UCPD_CFGR1_TRANSWIN_VAL(x) ((x) << \ + STM32_UCPD_CFGR1_TRANSWIN_SHIFT) +#define STM32_UCPD_CFGR1_PSC_CLK_SHIFT 17 +#define STM32_UCPD_CFGR1_PSC_CLK_MASK ((0x7) << \ + STM32_UCPD_CFGR1_PSC_CLK_SHIFT) +#define STM32_UCPD_CFGR1_PSC_CLK_VAL(x) ((x) << \ + STM32_UCPD_CFGR1_PSC_CLK_SHIFT) +#define STM32_UCPD_CFGR1_RXORDSETEN_SHIFT 20 +#define STM32_UCPD_CFGR1_RXORDSETEN_MASK ((0x1ff) << \ + STM32_UCPD_CFGR1_RXORDSETEN_SHIFT) +#define STM32_UCPD_CFGR1_RXORDSETEN_VAL(x) ((x) << \ + STM32_UCPD_CFGR1_RXORDSETEN_SHIFT) +#define STM32_UCPD_CFGR1_TXDMAEN BIT(29) +#define STM32_UCPD_CFGR1_RXDMAEN BIT(30) +#define STM32_UCPD_CFGR1_UCPDEN BIT(31) + +/* --- UCPD CFGR2 Bit Definitions --- */ +#define STM32_UCPD_CFGR2_RXFILTDIS BIT(0) +#define STM32_UCPD_CFGR2_RXFILT2N3 BIT(1) +#define STM32_UCPD_CFGR2_FORCECLK BIT(2) +#define STM32_UCPD_CFGR2_WUPEN BIT(3) + +/* --- UCPD CR Bit Definitions --- */ +#define STM32_UCPD_CR_TXMODE_SHIFT 0 +#define STM32_UCPD_CR_TXMODE_MASK ((0x3) << \ + (STM32_UCPD_CR_TXMODE_SHIFT)) +#define STM32_UCPD_CR_TXMODE_VAL(x) ((x) << STM32_UCPD_CR_TXMODE_SHIFT) +#define STM32_UCPD_CR_TXSEND BIT(2) +#define STM32_UCPD_CR_TXHRST BIT(3) +#define STM32_UCPD_CR_RXMODE BIT(4) +#define STM32_UCPD_CR_PHYRXEN BIT(5) +#define STM32_UCPD_CR_PHYCCSEL BIT(6) +#define STM32_UCPD_CR_ANASUBMODE_SHIFT 7 +#define STM32_UCPD_CR_ANASUBMODE_MASK ((0x3) << \ + (STM32_UCPD_CR_ANASUBMODE_SHIFT)) +#define STM32_UCPD_CR_ANASUBMODE_VAL(x) ((x) << \ + STM32_UCPD_CR_ANASUBMODE_SHIFT) +#define STM32_UCPD_CR_ANAMODE BIT(9) +#define STM32_UCPD_CR_CCENABLE_SHIFT 10 +#define STM32_UCPD_CR_CCENABLE_MASK ((0x3) << \ + (STM32_UCPD_CR_CCENABLE_SHIFT)) +#define STM32_UCPD_CR_CCENABLE_VAL(x) ((x) << \ + STM32_UCPD_CR_CCENABLE_SHIFT) +#define STM32_UCPD_CR_FRSRXEN BIT(16) +#define STM32_UCPD_CR_FRSTX BIT(17) +#define STM32_UCPD_CR_RDCH BIT(18) +#define STM32_UCPD_CR_CC1TCDIS BIT(20) +#define STM32_UCPD_CR_CC2TCDIS BIT(21) + +/* --- UCPD IMR Bit Definitions --- */ +#define STM32_UCPD_IMR_TXISIE BIT(0) +#define STM32_UCPD_IMR_TXMSGDISCIE BIT(1) +#define STM32_UCPD_IMR_TXMSGSENTIE BIT(2) +#define STM32_UCPD_IMR_TXMSGABTIE BIT(3) +#define STM32_UCPD_IMR_HRSTDISCIE BIT(4) +#define STM32_UCPD_IMR_HRSTSENTIE BIT(5) +#define STM32_UCPD_IMR_TXUNDIE BIT(6) +#define STM32_UCPD_IMR_RXNEIE BIT(8) +#define STM32_UCPD_IMR_RXORDDETIE BIT(9) +#define STM32_UCPD_IMR_RXHRSTDETIE BIT(10) +#define STM32_UCPD_IMR_RXOVRIE BIT(11) +#define STM32_UCPD_IMR_RXMSGENDIE BIT(12) +#define STM32_UCPD_IMR_TYPECEVT1IE BIT(14) +#define STM32_UCPD_IMR_TYPECEVT2IE BIT(15) +#define STM32_UCPD_IMR_FRSEVTIE BIT(20) + +/* --- UCPD SR Bit Definitions --- */ +#define STM32_UCPD_SR_TXIS BIT(0) +#define STM32_UCPD_SR_TXMSGDISC BIT(1) +#define STM32_UCPD_SR_TXMSGSENT BIT(2) +#define STM32_UCPD_SR_TXMSGABT BIT(3) +#define STM32_UCPD_SR_HRSTDISC BIT(4) +#define STM32_UCPD_SR_HRSTSENT BIT(5) +#define STM32_UCPD_SR_TXUND BIT(6) +#define STM32_UCPD_SR_RXNE BIT(8) +#define STM32_UCPD_SR_RXORDDET BIT(9) +#define STM32_UCPD_SR_RXHRSTDET BIT(10) +#define STM32_UCPD_SR_RXOVR BIT(11) +#define STM32_UCPD_SR_RXMSGEND BIT(12) +#define STM32_UCPD_SR_RXERR BIT(13) +#define STM32_UCPD_SR_TYPECEVT1 BIT(14) +#define STM32_UCPD_SR_TYPECEVT2 BIT(15) +#define STM32_UCPD_SR_VSTATE_CC1_SHIFT 16 +#define STM32_UCPD_SR_VSTATE_CC1_MASK ((0x3) << \ + (STM32_UCPD_SR_VSTATE_CC1_SHIFT)) +#define STM32_UCPD_SR_VSTATE_CC1_VAL(x) ((x) << \ + STM32_UCPD_SR_VSTATE_CC1_SHIFT) +#define STM32_UCPD_SR_VSTATE_CC2_SHIFT 18 +#define STM32_UCPD_SR_VSTATE_CC2_MASK ((0x3) << \ + (STM32_UCPD_SR_VSTATE_CC2_SHIFT)) +#define STM32_UCPD_SR_VSTATE_CC2_VAL(x) ((x) << \ + STM32_UCPD_SR_VSTATE_CC2_SHIFT) +#define STM32_UCPD_SR_FRSEVT BIT(20) + +#define STM32_UCPD_SR_VSTATE_OPEN 3 +#define STM32_UCPD_SR_VSTATE_RA 0 + +/* --- UCPD ICR Bit Definitions --- */ +#define STM32_UCPD_ICR_TXMSGDISCCF BIT(1) +#define STM32_UCPD_ICR_TXMSGSENTCF BIT(2) +#define STM32_UCPD_ICR_TXMSGABTCF BIT(3) +#define STM32_UCPD_ICR_HRSTDISCCF BIT(4) +#define STM32_UCPD_ICR_HRSTSENTCF BIT(5) +#define STM32_UCPD_ICR_TXUNDCF BIT(6) +#define STM32_UCPD_ICR_RXORDDETCF BIT(9) +#define STM32_UCPD_ICR_RXHRSTDETCF BIT(10) +#define STM32_UCPD_ICR_RXOVRCF BIT(11) +#define STM32_UCPD_ICR_RXMSGENDCF BIT(12) +#define STM32_UCPD_ICR_TYPECEVT1CF BIT(14) +#define STM32_UCPD_ICR_TYPECEVT2CF BIT(15) +#define STM32_UCPD_ICR_FRSEVTCF BIT(20) + + +/* --- UCPD TX_ORDSETR Bit Definitions --- */ +#define STM32_UCPD_TX_ORDSETR_SHIFT 0 +#define STM32_UCPD_TX_ORDSETR_MASK ((0xfffff) << \ + (STM32_UCPD_TX_ORDSETR_SHIFT)) +#define STM32_UCPD_TX_ORDSETR_VAL(x) ((x) << STM32_UCPD_TX_ORDSETR_SHIFT) + +/* --- UCPD TX_PAYSZR Bit Definitions --- */ +#define STM32_UCPD_TX_PAYSZR_SHIFT 0 +#define STM32_UCPD_TX_PAYSZR_MASK ((0x3ff) << \ + (STM32_UCPD_TX_PAYSZR_SHIFT)) +#define STM32_UCPD_TX_PAYSZR_VAL(x) ((x) << STM32_UCPD_TX_PAYSZR_SHIFT) + +/* --- UCPD TXDR Bit Definitions --- */ +#define STM32_UCPD_TXDR_SHIFT 0 +#define STM32_UCPD_TXDR_MASK ((0xff) << \ + (STM32_UCPD_TXDR_SHIFT)) +#define STM32_UCPD_TXDR_VAL(x) ((x) << STM32_UCPD_TXDR_SHIFT) + +/* --- UCPD RX_ORDSETR Bit Definitions --- */ +#define STM32_UCPD_RXORDSETR_SHIFT 0 +#define STM32_UCPD_RXORDSETR_MASK ((0x7) << \ + (STM32_UCPD_RXORDSETR_SHIFT)) +#define STM32_UCPD_RXORDSETR_VAL(x) ((x) << STM32_UCPD_RXORDSETR_SHIFT) +#define STM32_UCPD_RXSOP3OF4 BIT(3) +#define STM32_UCPD_RXSOPKINVALID_SHIFT 4 +#define STM32_UCPD_RXSOPKINVALID_MASK ((0x7) << \ + (STM32_UCPD_RXSOPKINVALID_SHIFT)) +#define STM32_UCPD_RXSOPKINVALID_VAL(x) ((x) << \ + STM32_UCPD_RXSOPKINVALID_SHIFT) + +/* --- UCPD RX_PAYSZR Bit Definitions --- */ +#define STM32_UCPD_RX_PAYSZR_SHIFT 0 +#define STM32_UCPD_RX_PAYSZR_MASK ((0x3ff) << \ + (STM32_UCPD_RX_PAYSZR_SHIFT)) +#define STM32_UCPD_RX_PAYSZR_VAL(x) ((x) << STM32_UCPD_RX_PAYSZR_SHIFT) + +/* --- UCPD TXDR Bit Definitions --- */ +#define STM32_UCPD_RXDR_SHIFT 0 +#define STM32_UCPD_RXDR_MASK ((0xff) << \ + (STM32_UCPD_RXDR_SHIFT)) +#define STM32_UCPD_RXDR_VAL(x) ((x) << STM32_UCPD_RXDR_SHIFT) + + /* --- USART --- */ #define STM32_USART_CR1(base) STM32_USART_REG(base, 0x00) #define STM32_USART_CR2(base) STM32_USART_REG(base, 0x04) @@ -508,6 +697,7 @@ /* --- RCC APB1ENR2 Bit Definitions --- */ #define STM32_RCC_APB1ENR2_LPUART1EN BIT(0) #define STM32_RCC_APB1ENR2_I2C4EN BIT(1) +#define STM32_RCC_APB1ENR2_UPCD1EN BIT(8) /* --- RCC APB2ENR Bit Definitions --- */ #define STM32_RCC_APB2ENR_SYSCFGEN BIT(0) @@ -591,6 +781,9 @@ #define STM32_PWR_RESET_CAUSE_CLR STM32_PWR_SCR #define RESET_CAUSE_SBF_CLR STM32_PWR_SCR_CSBF +#define STM32_PWR_CR3_UCPD1_STDBY BIT(13) +#define STM32_PWR_CR3_UCPD1_DBDIS BIT(14) + /* --- System Config Registers --- */ #define STM32_SYSCFG_MEMRMP REG32(STM32_SYSCFG_BASE + 0x00) #define STM32_SYSCFG_PMC REG32(STM32_SYSCFG_BASE + 0x04) diff --git a/chip/stm32/ucpd-stm32gx.c b/chip/stm32/ucpd-stm32gx.c new file mode 100644 index 0000000000..1355d9f042 --- /dev/null +++ b/chip/stm32/ucpd-stm32gx.c @@ -0,0 +1,277 @@ +/* Copyright 2020 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. + */ + +/* STM32GX UCPD module for Chrome EC */ + +#include "clock.h" +#include "common.h" +#include "gpio.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "ucpd-stm32gx.h" +#include "usb_pd.h" +#include "usb_pd_tcpm.h" +#include "util.h" + + +/* + * UCPD is fed directly from HSI which is @ 16MHz. The ucpd_clk goes to + * a prescaler who's output feeds the 'half-bit' divider which is used + * to generate clock for delay counters and BMC Rx/Tx blocks. The rx is + * designed to work in freq ranges of 6 <--> 18 MHz, however recommended + * range is 9 <--> 18 MHz. + * + * ------- @ 16 MHz --------- @ ~600 kHz ------------- + * HSI ---->| /psc |-------->| /hbit |--------------->| trans_cnt | + * ------- --------- | ------------- + * | ------------- + * |---------->| ifrgap_cnt| + * ------------- + * Requirements: + * 1. hbit_clk ~= 600 kHz: 16 MHz / 600 kHz = 26.67 + * 2. tTransitionWindow - 12 to 20 uSec + * 3. tInterframGap - uSec + * + * hbit_clk = HSI_clk / 26 = 615,385 kHz = 1.625 uSec period + * tTransitionWindow = 1.625 uS * 8 = 13 uS + * tInterFrameGap = 1.625 uS * 17 = 27.625 uS + */ +#define UCPD_PSC_DIV 1 +#define UCPD_HBIT_DIV 26 +#define UCPD_TRANSWIN_HBIT_CNT 8 +#define UCPD_IFRGAP_HBIT_CNT 17 + +#define UCPD_ANASUB_TO_RP(r) ((r - 1) & 0x3) +#define UCPD_RP_TO_ANASUB(r) ((r + 1) & 0x3) + +static void ucpd_port_enable(int port, int enable) +{ + if (enable) + STM32_UCPD_CFGR1(port) |= STM32_UCPD_CFGR1_UCPDEN; + else + STM32_UCPD_CFGR1(port) &= ~STM32_UCPD_CFGR1_UCPDEN; +} + +static int ucpd_is_cc_pull_active(int port, enum usbpd_cc_pin cc_line) +{ + int cc_enable = STM32_UCPD_CR(port) & STM32_UCPD_CR_CCENABLE_MASK >> + STM32_UCPD_CR_CCENABLE_SHIFT; + + return ((cc_enable >> cc_line) & 0x1); +} + +void stm32gx_ucpd1_irq(void) +{ + /* STM32_IRQ_UCPD indicates this is from UCPD1, so port = 0 */ + int port = 0; + uint32_t sr = STM32_UCPD_SR(port); + + if (sr & (STM32_UCPD_SR_TYPECEVT1 | STM32_UCPD_SR_TYPECEVT2)) { + task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_CC, 0); + } + /* Clear interrupts now that PD events have been set */ + STM32_UCPD_ICR(port) = sr; +} +DECLARE_IRQ(STM32_IRQ_UCPD1, stm32gx_ucpd1_irq, 1); + +int stm32gx_ucpd_init(int port) +{ + uint32_t cfgr1_reg; + + /* + * After exiting reset, stm32gx will have dead battery mode enabled by + * default which connects Rd to CC1/CC2. This should be disabled when EC + * is powered up. + */ + STM32_PWR_CR3 |= STM32_PWR_CR3_UCPD1_DBDIS; + + /* Ensure that clock to UCPD is enabled */ + STM32_RCC_APB1ENR2 |= STM32_RCC_APB1ENR2_UPCD1EN; + + /* + * CFGR1 must be written when UCPD peripheral is disabled. Note that + * disabling ucpd causes the peripheral to quit any ongoing activity and + * sets all ucpd registers back their default values. + */ + ucpd_port_enable(port, 0); + + cfgr1_reg = STM32_UCPD_CFGR1_PSC_CLK_VAL(UCPD_PSC_DIV - 1) | + STM32_UCPD_CFGR1_TRANSWIN_VAL(UCPD_TRANSWIN_CNT - 1) | + STM32_UCPD_CFGR1_IFRGAP_VAL(UCPD_IFRGAP_CNT - 1) | + STM32_UCPD_CFGR1_HBITCLKD_VAL(UCPD_HBIT_DIV - 1); + STM32_UCPD_CFGR1(port) = cfgr1_reg; + + /* Enable ucpd */ + ucpd_port_enable(port, 1); + + /* Configure CC change interrupts */ + STM32_UCPD_IMR(port) = STM32_UCPD_IMR_TYPECEVT1IE | + STM32_UCPD_IMR_TYPECEVT2IE; + STM32_UCPD_ICR(port) = STM32_UCPD_ICR_TYPECEVT1CF | + STM32_UCPD_ICR_TYPECEVT2CF; + + /* Enable UCPD interrupts */ + task_enable_irq(STM32_IRQ_UCPD1); + + return EC_SUCCESS; +} + +int stm32gx_ucpd_release(int port) +{ + ucpd_port_enable(port, 0); + + return EC_SUCCESS; +} + +int stm32gx_ucpd_get_cc(int port, enum tcpc_cc_voltage_status *cc1, + enum tcpc_cc_voltage_status *cc2) +{ + int vstate_cc1; + int vstate_cc2; + int anamode; + uint32_t sr; + + /* + * cc_voltage_status is determined from vstate_cc bit field in the + * status register. The meaning of the value vstate_cc depends on + * current value of ANAMODE (src/snk). + * + * vstate_cc maps directly to cc_state from tcpci spec when ANAMODE = 1, + * but needs to be modified slightly for case ANAMODE = 0. + * + * If presenting Rp (source), then need to to a circular shift of + * vstate_ccx value: + * vstate_cc | cc_state + * ------------------ + * 0 -> 1 + * 1 -> 2 + * 2 -> 0 + */ + + /* Get vstate_ccx values and power role */ + sr = STM32_UCPD_SR(port); + /* Get Rp or Rd active */ + anamode = !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_ANAMODE); + vstate_cc1 = (sr & STM32_UCPD_SR_VSTATE_CC1_MASK) >> + STM32_UCPD_SR_VSTATE_CC1_SHIFT; + vstate_cc2 = (sr & STM32_UCPD_SR_VSTATE_CC2_MASK) >> + STM32_UCPD_SR_VSTATE_CC2_SHIFT; + + /* Do circular shift if port == source */ + if (anamode) { + if (vstate_cc1 != STM32_UCPD_SR_VSTATE_RA) + vstate_cc1 += 4; + if (vstate_cc2 != STM32_UCPD_SR_VSTATE_RA) + vstate_cc2 += 4; + } else { + if (vstate_cc1 != STM32_UCPD_SR_VSTATE_OPEN) + vstate_cc1 = (vstate_cc1 + 1) % 3; + if (vstate_cc2 != STM32_UCPD_SR_VSTATE_OPEN) + vstate_cc2 = (vstate_cc2 + 1) % 3; + } + + *cc1 = vstate_cc1; + *cc2 = vstate_cc2; + + return EC_SUCCESS; +} + +int stm32gx_ucpd_get_role_control(int port) +{ + int role_control; + int cc1; + int cc2; + int anamode = !!(STM32_UCPD_CR(port) & STM32_UCPD_CR_ANAMODE); + int anasubmode = (STM32_UCPD_CR(port) & STM32_UCPD_CR_ANASUBMODE_MASK) + >> STM32_UCPD_CR_ANASUBMODE_SHIFT; + + /* + * Role control register is defined as: + * R_cc1 -> b 1:0 + * R_cc2 -> b 3:2 + * Rp -> b 5:4 + * + * In TCPCI, CCx is defined as: + * 00b -> Ra + * 01b -> Rp + * 10b -> Rd + * 11b -> Open (don't care) + * + * For ucpd, this information is encoded in ANAMODE and ANASUBMODE + * fields as follows: + * ANAMODE CCx + * 0 -> Rp -> 1 + * 1 -> Rd -> 2 + * + * ANASUBMODE: + * 00b -> TYPEC_RP_RESERVED (open) + * 01b -> TYPEC_RP_USB + * 10b -> TYPEC_RP_1A5 + * 11b -> TYPEC_RP_3A0 + * + * CCx = ANAMODE + 1, if CCx is enabled + * Rp = (ANASUBMODE - 1) & 0x3 + */ + cc1 = ucpd_is_cc_pull_active(port, USBPD_CC_PIN_1) ? anamode + 1 : + TYPEC_CC_OPEN; + cc2 = ucpd_is_cc_pull_active(port, USBPD_CC_PIN_2) ? anamode + 1 : + TYPEC_CC_OPEN; + role_control = cc1 | (cc2 << 2); + /* Circular shift anasubmode to convert to Rp range */ + role_control |= (UCPD_ANASUB_TO_RP(anasubmode) << 4); + + return role_control; +} + +int stm32gx_ucpd_set_cc(int port, int cc_pull, int rp) +{ + uint32_t cr = STM32_UCPD_CR(port); + + /* + * Always set ANASUBMODE to match desired Rp. TCPM layer has a valid + * range of 0, 1, or 2. This range maps to 1, 2, or 3 in ucpd for + * ANASUBMODE. + */ + cr &= ~STM32_UCPD_CR_ANASUBMODE_MASK; + cr |= STM32_UCPD_CR_ANASUBMODE_VAL(UCPD_RP_TO_ANASUB(rp)); + + /* Disconnect both pull from both CC lines by default */ + cr &= ~STM32_UCPD_CR_CCENABLE_MASK; + /* Set ANAMODE if cc_pull is Rd */ + if (cc_pull == TYPEC_CC_RD) { + cr |= STM32_UCPD_CR_ANAMODE | STM32_UCPD_CR_CCENABLE_MASK; + /* Clear ANAMODE if cc_pull is Rp */ + } else if (cc_pull == TYPEC_CC_RP) { + cr &= ~(STM32_UCPD_CR_ANAMODE); + cr |= STM32_UCPD_CR_CCENABLE_MASK; + } + + /* Update pull values */ + STM32_UCPD_CR(port) = cr; + + /* TODO(b/): Should this return error if cc_pull == Ra */ + return EC_SUCCESS; +} + +int stm32gx_ucpd_set_polarity(int port, enum tcpc_cc_polarity polarity) +{ + /* + * Polarity impacts the PHYCCSEL, CCENABLE, and CCxTCDIS fields. This + * function is called when polarity is updated at TCPM layer. STM32Gx + * only supports POLARITY_CC1 or POLARITY_CC2 and this is stored in the + * PHYCCSEL bit in the CR register. + */ + if (polarity > POLARITY_CC2) + return EC_ERROR_UNIMPLEMENTED; + + if (polarity == POLARITY_CC1) + STM32_UCPD_CR(port) &= ~STM32_UCPD_CR_PHYCCSEL; + else if (polarity == POLARITY_CC2) + STM32_UCPD_CR(port) |= STM32_UCPD_CR_PHYCCSEL; + + return EC_SUCCESS; +} + diff --git a/chip/stm32/ucpd-stm32gx.h b/chip/stm32/ucpd-stm32gx.h new file mode 100644 index 0000000000..f0a73cb29e --- /dev/null +++ b/chip/stm32/ucpd-stm32gx.h @@ -0,0 +1,111 @@ +/* Copyright 2020 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. + */ +#ifndef __CROS_EC_UCPD_STM32GX_H +#define __CROS_EC_UCPD_STM32GX_H + +/* STM32 UCPD driver for Chrome EC */ + +#include "usb_pd_tcpm.h" + +/* + * K-codes and ordered set defines. These codes and sets are used to encode + * which type of USB-PD message is being sent. This information can be found in + * the USB-PD spec section 5.4 - 5.6. This info is also included in the STM32G4 + * TRM (RM0440) 45.4.3 + */ +#define UCPD_SYNC1 0x18u +#define UCPD_SYNC2 0x11u +#define UCPD_SYNC3 0x06u +#define UCPD_RST1 0x07u +#define UCPD_RST2 0x19u +#define UCPD_EOP 0x0Du + +enum ucpd_tx_ordset { + TX_ORDERSET_SOP = (UCPD_SYNC1 | + (UCPD_SYNC1<<5u) | + (UCPD_SYNC1<<10u) | + (UCPD_SYNC2<<15u)), + TX_ORDERSET_SOP1 = (UCPD_SYNC1 | + (UCPD_SYNC1<<5u) | + (UCPD_SYNC3<<10u) | + (UCPD_SYNC3<<15u)), + TX_ORDERSET_SOP2 = (UCPD_SYNC1 | + (UCPD_SYNC3<<5u) | + (UCPD_SYNC1<<10u) | + (UCPD_SYNC3<<15u)), + TX_ORDERSET_HARD_RESET = (UCPD_RST1 | + (UCPD_RST1<<5u) | + (UCPD_RST1<<10u) | + (UCPD_RST2<<15u)), + TX_ORDERSET_CABLE_RESET = + (UCPD_RST1 | + (UCPD_SYNC1<<5u) | + (UCPD_RST1<<10u) | + (UCPD_SYNC3<<15u)), + TX_ORDERSET_SOP1_DEBUG = (UCPD_SYNC1 | + (UCPD_RST2<<5u) | + (UCPD_RST2<<10u) | + (UCPD_SYNC3<<15u)), + TX_ORDERSET_SOP2_DEBUG = (UCPD_SYNC1 | + (UCPD_RST2<<5u) | + (UCPD_SYNC3<<10u) | + (UCPD_SYNC2<<15u)), +}; + +/** + * STM32Gx UCPD implementation of tcpci .init method + * + * @param usbc_port -> USB-C Port number + * @return EC_SUCCESS + */ +int stm32gx_ucpd_init(int usbc_port); + +/** + * STM32Gx UCPD implementation of tcpci .release method + * + * @param usbc_port -> USB-C Port number + * @return EC_SUCCESS + */ +int stm32gx_ucpd_release(int usbc_port); + +/** + * STM32Gx UCPD implementation of tcpci .get_cc method + * + * @param usbc_port -> USB-C Port number + * @param *cc1 -> pointer to cc1 result + * @param *cc2 -> pointer to cc2 result + * @return EC_SUCCESS + */ +int stm32gx_ucpd_get_cc(int usbc_port, enum tcpc_cc_voltage_status *cc1, + enum tcpc_cc_voltage_status *cc2); + +/** + * STM32Gx equivalent for TCPCI role_control register + * + * @param usbc_port -> USB-C Port number + * @return EC_SUCCESS + */ +int stm32gx_ucpd_get_role_control(int usbc_port); + +/** + * STM32Gx UCPD implementation of tcpci .set_cc method + * + * @param usbc_port -> USB-C Port number + * @param cc_pull -> Rp or Rd selection + * @param rp -> value of Rp (if cc_pull == Rp) + * @return EC_SUCCESS + */ +int stm32gx_ucpd_set_cc(int usbc_port, int cc_pull, int rp); + +/** + * STM32Gx UCPD implementation of tcpci .set_cc method + * + * @param usbc_port -> USB-C Port number + * @param polarity -> CC1 or CC2 selection + * @return EC_SUCCESS + */ +int stm32gx_ucpd_set_polarity(int usbc_port, enum tcpc_cc_polarity polarity); + +#endif /* __CROS_EC_UCPD_STM32GX_H */ |